-
dockx thibault authored
Removes typo in condition entry, '-' was missing.
Verified9fc0ccea
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2003-2010 Cajus Pollmeier
Copyright (C) 2011-2019 FusionDirectory
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 St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* \file class_SnapshotHandler
* Source code for class SnapshotHandler
*/
/*!
* \brief This class contains all the function needed to handle
* the snapshot functionality
*/
class SnapshotHandler
{
protected $enabled;
protected $snapshotRDN;
protected $snapshotsCache;
static function plInfo ()
{
return [
'plShortName' => _('Snapshot'),
'plDescription' => _('Snapshot handler'),
/* Categories for snapshots are computed in config class */
'plCategory' => [],
'plProvidedAcls' => [
'restore_over' => _('Restore over an existing object'),
'restore_deleted' => _('Restore a deleted object'),
]
];
}
/*!
* \brief Create handler
*/
function __construct ()
{
global $config;
$this->enabled = $config->snapshotEnabled();
if ($this->enabled) {
/* Prepare base */
$this->snapshotRDN = $config->get_cfg_value('snapshotBase');
$ldap = $config->get_ldap_link();
$ldap->cd($config->current['BASE']);
try {
$ldap->create_missing_trees($this->snapshotRDN);
} catch (FusionDirectoryError $error) {
$error->display();
}
}
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
}
/*!
* \brief Check if the snapshot is enable
*
* \return boolean TRUE if is enable, return FALSE otherwise
*/
function enabled ()
{
return $this->enabled;
}
/* \brief Get the snapshot dn of an object dn
*/
protected function snapshot_dn ($dn)
{
global $config;
return preg_replace("/".preg_quote($config->current['BASE'], '/')."$/", "", $dn)
.$this->snapshotRDN;
}
/*!
* \brief Check if there are deleted snapshots
*/
function hasDeletedSnapshots ($bases)
{
foreach ($bases as $base) {
if (count($this->getAllDeletedSnapshots($base)) > 0) {
return TRUE;
}
}
return FALSE;
}
/*!
* \brief Cache Snapshot information for all objects in $base
*/
function initSnapshotCache ($base)
{
global $config;
if (!$this->enabled()) {
return;
}
$ldap = $config->get_ldap_link();
// Initialize base
$base = $this->snapshot_dn($base);
/* Fetch all objects with */
$ldap->cd($base);
$ldap->search('(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=*))', ['gosaSnapshotDN']);
/* Store for which object we have snapshots */
$this->snapshotsCache = [];
while ($entry = $ldap->fetch()) {
$this->snapshotsCache[$entry['gosaSnapshotDN'][0]] = TRUE;
}
}
/*!
* \brief Check if the DN has snapshots
*
* \return the numbers of snapshots
*/
function hasSnapshots ($dn)
{
return isset($this->snapshotsCache[$dn]);
}
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
/*!
* \brief Get snapshots
*
* \param string $dn The DN
*
* \param string $raw FALSE
*/
function getSnapshots ($dn, $raw = FALSE)
{
global $config;
if (!$this->enabled()) {
return [];
}
$ldap = $config->get_ldap_link();
$objectBase = preg_replace("/^[^,]*./", "", $dn);
// Initialize base
$base = $this->snapshot_dn($objectBase);
/* Fetch all objects with gosaSnapshotDN=$dn */
$ldap->cd($base);
$ldap->search(
'(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN='.ldap_escape_f($dn).'))',
['gosaSnapshotTimestamp','gosaSnapshotDN','description'],
'one'
);
/* Put results into a list and add description if missing */
$objects = [];
while ($entry = $ldap->fetch(TRUE)) {
if (!isset($entry['description'][0])) {
$entry['description'][0] = "";
}
$objects[] = $entry;
}
/* Return the raw array, or format the result */
if ($raw) {
return $objects;
} else {
$tmp = [];
foreach ($objects as $entry) {
$tmp[base64_encode($entry['dn'])] = $entry['description'][0];
}
}
return $tmp;
}
/*!
* \brief Create a snapshot of the current object
*
* \param string $dn The DN
*
* \param string $description Snapshot description
*
* \param string $objectType Type of snapshotted object
*
* \param string $snapshotSource source of the data.
*/
function createSnapshot ($dn, string $description, string $objectType, string $snapshotSource = 'FD')
{
global $config;
if (!$this->enabled()) {
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot are disabled but tried to create snapshot');
return;
}
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
if (is_array($dn)) {
$dns = $dn;
$dn = $dns[0];
} else {
$dns = [$dn];
}
$ldap = $config->get_ldap_link();
/* check if the dn exists */
if (!$ldap->dn_exists($dn)) {
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Tried to snapshot non-existing dn');
return;
}
/* Extract seconds & mysecs, they are used as entry index */
list($usec, $sec) = explode(" ", microtime());
/* Collect some infos */
$base_of_object = preg_replace('/^[^,]+,/i', '', $dn);
$new_base = $this->snapshot_dn($base_of_object);
/* Create object */
$data = '';
foreach ($dns as $tmp_dn) {
try {
$data .= $ldap->generateLdif($tmp_dn, '(!(objectClass=gosaDepartment))', 'sub');
} catch (LDIFExportException $e) {
$error = new FusionDirectoryError(
htmlescape(sprintf(
_('Failed to create snapshot: %s'),
$e->getMessage()
))
);
$error->display();
return;
}
}
$target = [];
$target['objectClass'] = ['top', 'gosaSnapshotObject'];
$target['gosaSnapshotData'] = gzcompress($data, 6);
$target['gosaSnapshotDN'] = $dn;
$target['description'] = $description;
$target['fdSnapshotObjectType'] = $objectType;
$target['fdSnapshotDataSource'] = $snapshotSource;
/* Insert the new snapshot
But we have to check first, if the given gosaSnapshotTimestamp
is already used, in this case we should increment this value till there is
an unused value. */
do {
$target['gosaSnapshotTimestamp'] = str_replace('.', '', $sec.'-'.$usec);
$new_dn = 'gosaSnapshotTimestamp='.$target['gosaSnapshotTimestamp'].','.$new_base;
$ldap->cat($new_dn);
$usec++;
} while ($ldap->count());
/* Insert this new snapshot */
$ldap->cd($this->snapshotRDN);
try {
$ldap->create_missing_trees($this->snapshotRDN);
$ldap->create_missing_trees($new_base);
} catch (FusionDirectoryError $error) {
$error->display();
}
$ldap->cd($new_dn);
$ldap->add($target);
if (!$ldap->success()) {
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
$error = new FusionDirectoryLdapError($new_dn, LDAP_ADD, $ldap->get_error(), $ldap->get_errno());
$error->display();
}
logging::log('snapshot', 'create', $new_dn, array_keys($target), $ldap->get_error());
}
// function verifing the configuration retention for snapshots.
// Remove snapshots from the user if retention rules approves.
public function verifySnapshotRetention (string $dn) : void
{
global $config;
// In case the snap configuration has not set any numbers
if (isset($config->current['SNAPSHOTMINRETENTION']) && !empty($config->current['SNAPSHOTMINRETENTION'])) {
$snapMinRetention = $config->current['SNAPSHOTMINRETENTION'];
} else {
$snapMinRetention = 0;
}
if (isset($config->current['SNAPSHOTRETENTIONDAYS']) && !empty($config->current['SNAPSHOTRETENTIONDAYS'])) {
$snapRetentionDays = $config->current['SNAPSHOTRETENTIONDAYS'];
} else {
$snapRetentionDays = -1;
}
// calculate the epoch date on which snaps can be delete.
if ($snapRetentionDays !== -1) {
$todayMinusRetention = time() - ($snapRetentionDays * 24 * 60 * 60);
$snapDateToDelete = strtotime(date('Y-m-d H:i:s', $todayMinusRetention));
$dnSnapshotsList = $this->getSnapshots($dn, TRUE);
$snapToDelete = [];
$snapCount = 0;
// Generate an arrays with snapshot to delete due to overdate.
if (isset($dnSnapshotsList) && !empty($dnSnapshotsList)) {
foreach ($dnSnapshotsList as $snap) {
$snapCount += 1;
// let's keep seconds instead of nanosecs
$snapEpoch = preg_split('/-/', $snap['gosaSnapshotTimestamp'][0]);
if ($snapEpoch[0] < $snapDateToDelete) {
$snapToDelete[] = $snap['dn'];
}
}
}
// The not empty is not mandatory but is more ressource friendly
if (!empty($snapToDelete) && ($snapCount > $snapMinRetention)) {
$snapToKeep = $snapCount - $snapMinRetention;
// Sort snapToDelete by old first DN timestamp is the only thing different.
sort($snapToDelete);
for ($i = 0; $i < $snapToKeep; $i++) {
// not empty required because array keeps on being iterated even if NULL object.
if (!empty($snapToDelete[$i])) {
$this->removeSnapshot($snapToDelete[$i]);
}
}
}
}
}
/*!
* \brief Remove a snapshot
*
* \param string $dn The DN
*/
function removeSnapshot ($dn)
{
global $config;
$ldap = $config->get_ldap_link();
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
$ldap->cd($config->current['BASE']);
$ldap->rmdir_recursive($dn);
if (!$ldap->success()) {
$error = new FusionDirectoryLdapError($dn, LDAP_DEL, $ldap->get_error(), $ldap->get_errno());
$error->display();
}
logging::log('snapshot', 'delete', $dn, [], $ldap->get_error());
}
/*!
* \brief Get the available snapshots
*
* \return available snapshots for the given base
*/
function getAvailableSnapsShots ($dn)
{
global $config;
if (!$this->enabled()) {
return [];
}
$ldap = $config->get_ldap_link();
/* Prepare bases and some other infos */
$base_of_object = preg_replace('/^[^,]+,/i', '', $dn);
$new_base = $this->snapshot_dn($base_of_object);
$tmp = [];
/* Fetch all objects with gosaSnapshotDN=$dn */
$ldap->cd($new_base);
$ldap->search(
'(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN='.ldap_escape_f($dn).'))',
['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType'],
'one'
);
/* Put results into a list and add description if missing */
while ($entry = $ldap->fetch(TRUE)) {
if (!isset($entry['description'][0])) {
$entry['description'][0] = "";
}
$tmp[] = $entry;
}
return $tmp;
}
/*!
* \brief Get all deleted snapshots
*
* \param string $base_of_object
*/
function getAllDeletedSnapshots ($base_of_object)
{
global $config;
if (!$this->enabled()) {
return [];
}
$ldap = $config->get_ldap_link();
/* Prepare bases */
$new_base = $this->snapshot_dn($base_of_object);
/* Fetch all objects and check if they do not exist anymore */
$tmp = [];
$ldap->cd($new_base);
$ldap->search(
'(objectClass=gosaSnapshotObject)',
['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType'],
'one'
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
);
while ($entry = $ldap->fetch(TRUE)) {
$chk = str_replace($new_base, "", $entry['dn']);
if (preg_match("/,ou=/", $chk)) {
continue;
}
if (!isset($entry['description'][0])) {
$entry['description'][0] = "";
}
$tmp[] = $entry;
}
/* Check if entry still exists */
foreach ($tmp as $key => $entry) {
if ($ldap->dn_exists($entry['gosaSnapshotDN'][0])) {
unset($tmp[$key]);
}
}
return $tmp;
}
/*!
* \brief Restore selected snapshot
*
* \param string $dn The DN
*/
function restoreSnapshot ($dn)
{
global $config;
if (!$this->enabled()) {
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot are disabled but tried to restore snapshot');
return FALSE;
}
$ldap = $config->get_ldap_link();
/* Get the snapshot */
$ldap->cat($dn, ['gosaSnapshotData','gosaSnapshotDN'], '(gosaSnapshotData=*)');
if ($attrs = $ldap->fetch()) {
/* Prepare import string */
$data = gzuncompress($attrs['gosaSnapshotData'][0]);
if ($data === FALSE) {
$error = new FusionDirectoryError(htmlescape(_('There was a problem uncompressing snapshot data')));
$error->display();
return FALSE;
}
} else {
$error = new FusionDirectoryError(htmlescape(_('Snapshot data could not be fetched')));
$error->display();
return FALSE;
}
/* Import the given data */
try {
$ldap->import_complete_ldif($data, FALSE, FALSE);
logging::log('snapshot', 'restore', $dn, [], $ldap->get_error());
if (!$ldap->success()) {
$error = new FusionDirectoryLdapError($dn, NULL, $ldap->get_error(), $ldap->get_errno());
$error->display();
return FALSE;
}
return $attrs['gosaSnapshotDN'][0];
} catch (LDIFImportException $e) {
$error = new FusionDirectoryError($e->getMessage(), 0, $e);
$error->display();
logging::log('snapshot', 'restore', $dn, [], $e->getMessage());
return FALSE;
491492493494
}
}
}