diff --git a/lam/HISTORY b/lam/HISTORY
index cacc030c..aeff1aba 100644
--- a/lam/HISTORY
+++ b/lam/HISTORY
@@ -1,5 +1,7 @@
August 2011 3.5.0
- - New module "General information": shows internal data about accounts (e.g. creation time)
+ - New modules:
+ -> "General information": shows internal data about accounts (e.g. creation time)
+ -> "Quota": manage filesystem quota inside LDAP (Linux DiskQuota) (RFE 1811449)
- inetOrgPerson: New attributes o, employeeNumber, initials
- Unix: Support to create home directories on multiple servers and also for existing users
- Server information shows data from cn=monitor
diff --git a/lam/docs/manual-sources/howto.xml b/lam/docs/manual-sources/howto.xml
index 255c68de..74938409 100644
--- a/lam/docs/manual-sources/howto.xml
+++ b/lam/docs/manual-sources/howto.xml
@@ -1651,12 +1651,19 @@ Have fun!
- Quota
+ Filesystem quota (lamdaemon)
You can manage file system quotas with LAM. This requires to
- setup lamdaemon. File system quotas
- are not stored inside LAM but managed directly on the specified
- servers.
+ setup lamdaemon. LAM connects to
+ your server via SSH and manages the disk filesystem quotas. The quotas
+ are stored directly on the filesystem. This is the default mechanism
+ to store quotas for most systems.
+
+ Please add the module "Quota (quota)" for users to your LAM
+ server profile to enable this feature.
+
+ If you store the quota information directly inside LDAP please
+ see the next section.
@@ -1667,6 +1674,30 @@ Have fun!
+
+ Filesystem quota (LDAP)
+
+ You can store your filesystem quotas directly in LDAP. See
+ Linux
+ DiskQuota for details since it requires quota tools that
+ support LDAP. You will need to install the quota LDAP schema to manage
+ the object class "systemQuotas".
+
+ Please add the module "Quota (systemQuotas)" for users to your
+ LAM server profile to enable this feature.
+
+ If you store the quota information on the filesystem please see
+ the previous section.
+
+
+
+
+
+
+
+
+
+
Kolab
@@ -3724,6 +3755,26 @@ Have fun!
+
+
+
+
+
+
+
+ Filesystem quotas
+
+ systemQuotas
+
+ quota.schema
+
+ Linux
+ DiskQuota
+
+
+
+
@@ -3739,7 +3790,7 @@ Have fun!
Part of OpenLDAP installation
- These account type is only available in LAM Pro.
+ This account type is only available in LAM Pro.
diff --git a/lam/docs/manual-sources/images/mod_systemQuotas.png b/lam/docs/manual-sources/images/mod_systemQuotas.png
new file mode 100644
index 00000000..61117964
Binary files /dev/null and b/lam/docs/manual-sources/images/mod_systemQuotas.png differ
diff --git a/lam/docs/manual-sources/images/schema_quota.png b/lam/docs/manual-sources/images/schema_quota.png
new file mode 100644
index 00000000..7e8d7017
Binary files /dev/null and b/lam/docs/manual-sources/images/schema_quota.png differ
diff --git a/lam/lib/modules/systemQuotas.inc b/lam/lib/modules/systemQuotas.inc
new file mode 100644
index 00000000..72150c04
--- /dev/null
+++ b/lam/lib/modules/systemQuotas.inc
@@ -0,0 +1,378 @@
+ array('posixAccount'), 'conflicts' => array());
+ // managed object classes
+ $return['objectClasses'] = array('systemQuotas');
+ // managed attributes
+ $return['attributes'] = array('quota');
+ // help Entries
+ $return['help'] = array(
+ 'quota' => array(
+ "Headline" => _("Quota"),
+ "Text" => _("Please enter the quota settings for this user. The syntax is: {mount point},{soft block limit},{hard block limit},{soft inode limit},{hard inode limit}.")
+ . ' ' . _('Multiple values are separated by semicolon.')
+ )
+ );
+ // profile elements
+ $profileContainer = new htmlTable();
+ $profileContainer->addElement(new htmlTableExtendedInputField(_('Quota'), 'systemQuotas_quota', null, 'quota'));
+ $return['profile_options'] = $profileContainer;
+ // upload fields
+ $return['upload_columns'] = array(
+ array(
+ 'name' => 'systemQuotas_quota',
+ 'description' => _('Quota'),
+ 'help' => 'quota',
+ 'example' => '/home/smiller,50000,60000,10000,12000',
+ )
+ );
+ // available PDF fields
+ $return['PDF_fields'] = array(
+ 'quota' => _('Quota')
+ );
+ return $return;
+ }
+
+ /**
+ * This function fills the $messages variable with output messages from this module.
+ */
+ public function load_Messages() {
+ $this->messages['path'][0] = array('ERROR', _('Mountpoint'), _('Mountpoint contains invalid characters.'));
+ $this->messages['path'][1] = array('ERROR', _('Account %s:'), _('Mountpoint contains invalid characters.'));
+ $this->messages['softblock'][0] = array('ERROR', _('Block soft quota'), _('Block soft quota contains invalid characters. Only natural numbers are allowed.'));
+ $this->messages['softblock'][1] = array('ERROR', _('Account %s:'), _('Block soft quota contains invalid characters. Only natural numbers are allowed.'));
+ $this->messages['hardblock'][0] = array('ERROR', _('Block hard quota'), _('Block hard quota contains invalid characters. Only natural numbers are allowed.'));
+ $this->messages['hardblock'][1] = array('ERROR', _('Account %s:'), _('Block hard quota contains invalid characters. Only natural numbers are allowed.'));
+ $this->messages['softinode'][0] = array('ERROR', _('Inode soft quota'), _('Inode soft quota contains invalid characters. Only natural numbers are allowed.'));
+ $this->messages['softinode'][1] = array('ERROR', _('Account %s:'), _('Inode soft quota contains invalid characters. Only natural numbers are allowed.'));
+ $this->messages['hardinode'][0] = array('ERROR', _('Inode hard quota'), _('Inode hard quota contains invalid characters. Only natural numbers are allowed.'));
+ $this->messages['hardinode'][1] = array('ERROR', _('Account %s:'), _('Inode hard quota contains invalid characters. Only natural numbers are allowed.'));
+ $this->messages['block_cmp'][0] = array('ERROR', _('Block quota'), _('Block soft quota must be smaller than block hard quota.'));
+ $this->messages['block_cmp'][1] = array('ERROR', _('Account %s:'), _('Block soft quota must be smaller than block hard quota.'));
+ $this->messages['inode_cmp'][0] = array('ERROR', _('Inode quota'), _('Inode soft quota must be smaller than inode hard quota.'));
+ $this->messages['inode_cmp'][1] = array('ERROR', _('Account %s:'), _('Inode soft quota must be smaller than inode hard quota.'));
+ }
+
+ /**
+ * Returns the HTML meta data for the main account page.
+ *
+ * @return htmlElement HTML meta data
+ */
+ public function display_html_attributes() {
+ $container = new htmlTable();
+ $spacer = new htmlSpacer('10px', null);
+ // caption
+ $container->addElement(new htmlOutputText(_('Mountpoint')));
+ $container->addElement($spacer);
+ $container->addElement(new htmlOutputText(_('Soft block limit')));
+ $container->addElement($spacer);
+ $container->addElement(new htmlOutputText(_('Hard block limit')));
+ $container->addElement($spacer);
+ $container->addElement(new htmlOutputText(_('Soft inode limit')));
+ $container->addElement($spacer);
+ $container->addElement(new htmlOutputText(_('Hard inode limit')), true);
+ // existing entries
+ if (isset($this->attributes['quota'][0])) {
+ natcasesort($this->attributes['quota']);
+ $this->attributes['quota'] = array_values($this->attributes['quota']);
+ for ($i = 0; $i < sizeof($this->attributes['quota']); $i++) {
+ $parts = explode(',', $this->attributes['quota'][$i]);
+ $container->addElement(new htmlInputField('path_' . $i, $parts[0], 20));
+ $container->addElement($spacer);
+ $container->addElement(new htmlInputField('softBlock_' . $i, $parts[1], 10));
+ $container->addElement($spacer);
+ $container->addElement(new htmlInputField('hardBlock_' . $i, $parts[2], 10));
+ $container->addElement($spacer);
+ $container->addElement(new htmlInputField('softInode_' . $i, $parts[3], 10));
+ $container->addElement($spacer);
+ $container->addElement(new htmlInputField('hardInode_' . $i, $parts[4], 10));
+ $container->addElement(new htmlButton('del_' . $i, 'del.png', true), true);
+ }
+ }
+ // new entry
+ $container->addElement(new htmlInputField('path', null, 20));
+ $container->addElement($spacer);
+ $container->addElement(new htmlInputField('softBlock', 0, 10));
+ $container->addElement($spacer);
+ $container->addElement(new htmlInputField('hardBlock', 0, 10));
+ $container->addElement($spacer);
+ $container->addElement(new htmlInputField('softInode', 0, 10));
+ $container->addElement($spacer);
+ $container->addElement(new htmlInputField('hardInode', 0, 10));
+ $container->addElement(new htmlButton('add', 'add.png', true));
+ return $container;
+ }
+
+ /**
+ * Processes user input of the primary module page.
+ * It checks if all input values are correct and updates the associated LDAP attributes.
+ *
+ * @return array list of info/error messages
+ */
+ public function process_attributes() {
+ $return = array();
+ if (!isset($this->attributes['quota'][0])) {
+ $this->attributes['quota'] = array();
+ }
+ // check existing entries
+ for ($i = 0; $i < sizeof($this->attributes['quota']); $i++) {
+ if (isset($_POST['del_' . $i])) {
+ unset($this->attributes['quota'][$i]);
+ $this->attributes['quota'] = array_values($this->attributes['quota']);
+ $i--;
+ continue;
+ }
+ $path = $_POST['path_' . $i];
+ $softBlock = $_POST['softBlock_' . $i];
+ if ($softBlock == '') $softBlock = '0';
+ $hardBlock = $_POST['hardBlock_' . $i];
+ if ($hardBlock == '') $hardBlock = '0';
+ $softInode = $_POST['softInode_' . $i];
+ if ($softInode == '') $softInode = '0';
+ $hardInode = $_POST['hardInode_' . $i];
+ if ($hardInode == '') $hardInode = '0';
+ $this->attributes['quota'][$i] = $path . ',' . $softBlock . ',' . $hardBlock . ',' .
+ $softInode . ',' . $hardInode;
+ $return = array_merge($return, $this->checkQuota($path, $softBlock, $hardBlock, $softInode, $hardInode));
+ }
+ // check for new entry
+ if (isset($_POST['add'])) {
+ $path = $_POST['path'];
+ $softBlock = $_POST['softBlock'];
+ if ($softBlock == '') $softBlock = '0';
+ $hardBlock = $_POST['hardBlock'];
+ if ($hardBlock == '') $hardBlock = '0';
+ $softInode = $_POST['softInode'];
+ if ($softInode == '') $softInode = '0';
+ $hardInode = $_POST['hardInode'];
+ if ($hardInode == '') $hardInode = '0';
+ $this->attributes['quota'][] = $path . ',' . $softBlock . ',' . $hardBlock . ',' .
+ $softInode . ',' . $hardInode;
+ $return = array_merge($return, $this->checkQuota($path, $softBlock, $hardBlock, $softInode, $hardInode));
+ }
+ $this->attributes['quota'] = array_unique($this->attributes['quota']);
+ return $return;
+ }
+
+ /**
+ * Checks if the quota parameters are valid.
+ *
+ * @param String $path mountpoint
+ * @param int $softBlock soft block limit
+ * @param int $hardBlock hard block limit
+ * @param int $softInode soft inode limit
+ * @param int $hardInode hard inode limit
+ * @param boolean $uploadIndex position is upload table
+ * @return array array where error messages are returned
+ */
+ private function checkQuota($path, $softBlock, $hardBlock, $softInode, $hardInode, $uploadIndex = null) {
+ $return = array();
+ if (!get_preg($path, 'filePath')) {
+ if ($uploadIndex == null) {
+ $return[] = $this->messages['path'][0];
+ }
+ else {
+ $error = $this->messages['path'][1];
+ $error[] = array($uploadIndex);
+ $return[] = $error;
+ }
+ }
+ if (!get_preg($softBlock, 'digit')) {
+ if ($uploadIndex == null) {
+ $return[] = $this->messages['softblock'][0];
+ }
+ else {
+ $error = $this->messages['softblock'][1];
+ $error[] = array($uploadIndex);
+ $return[] = $error;
+ }
+ }
+ if (!get_preg($hardBlock, 'digit')) {
+ if ($uploadIndex == null) {
+ $return[] = $this->messages['hardblock'][0];
+ }
+ else {
+ $error = $this->messages['hardblock'][1];
+ $error[] = array($uploadIndex);
+ $return[] = $error;
+ }
+ }
+ if (!get_preg($softInode, 'digit')) {
+ if ($uploadIndex == null) {
+ $return[] = $this->messages['softinode'][0];
+ }
+ else {
+ $error = $this->messages['softinode'][1];
+ $error[] = array($uploadIndex);
+ $return[] = $error;
+ }
+ }
+ if (!get_preg($hardInode, 'digit')) {
+ if ($uploadIndex == null) {
+ $return[] = $this->messages['hardinode'][0];
+ }
+ else {
+ $error = $this->messages['hardinode'][1];
+ $error[] = array($uploadIndex);
+ $return[] = $error;
+ }
+ }
+ if ($softBlock > $hardBlock) {
+ if ($uploadIndex == null) {
+ $return[] = $this->messages['block_cmp'][0];
+ }
+ else {
+ $error = $this->messages['block_cmp'][1];
+ $error[] = array($uploadIndex);
+ $return[] = $error;
+ }
+ }
+ if ($softInode > $hardInode) {
+ if ($uploadIndex == null) {
+ $return[] = $this->messages['inode_cmp'][0];
+ }
+ else {
+ $error = $this->messages['inode_cmp'][1];
+ $error[] = array($uploadIndex);
+ $return[] = $error;
+ }
+ }
+ return $return;
+ }
+
+ /**
+ * Checks input values of account profiles.
+ *
+ * @param array $options a hash array (name => value) containing the options
+ * @return array list of error messages (array(type, title, text)) to generate StatusMessages, if any
+ */
+ function check_profileOptions($options) {
+ $messages = parent::check_profileOptions($options);
+ $quotas = explode(';', $options['systemQuotas_quota'][0]);
+ for ($q = 0; $q < sizeof($quotas); $q++) {
+ $parts = explode(',', $quotas[$q]);
+ $messages = array_merge($messages, $this->checkQuota($parts[0], $parts[1], $parts[2], $parts[3], $parts[4]));
+ }
+ return $messages;
+ }
+
+ /**
+ * Loads the values of an account profile into internal variables.
+ *
+ * @param array $profile hash array with profile values (identifier => value)
+ */
+ function load_profile($profile) {
+ // profile mappings in meta data
+ parent::load_profile($profile);
+ if (isset($profile['systemQuotas_quota'][0]) && ($profile['systemQuotas_quota'][0] != '')) {
+ $this->attributes['quota'] = explode(';', $profile['systemQuotas_quota'][0]);
+ }
+ }
+
+ /**
+ * In this function the LDAP account is built up.
+ *
+ * @param array $rawAccounts list of hash arrays (name => value) from user input
+ * @param array $partialAccounts list of hash arrays (name => value) which are later added to LDAP
+ * @param array $ids list of IDs for column position (e.g. "posixAccount_uid" => 5)
+ * @param array $selectedModules list of selected account modules
+ * @return array list of error messages if any
+ */
+ public function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules) {
+ $messages = array();
+ for ($i = 0; $i < sizeof($rawAccounts); $i++) {
+ // add object class
+ if (!in_array('systemQuotas', $partialAccounts[$i]['objectClass'])) $partialAccounts[$i]['objectClass'][] = 'systemQuotas';
+ // add quota
+ if (isset($rawAccounts[$i][$ids['systemQuotas_quota']]) && ($rawAccounts[$i][$ids['systemQuotas_quota']] != '')) {
+ $quotas = explode(';', $rawAccounts[$i][$ids['systemQuotas_quota']]);
+ for ($q = 0; $q < sizeof($quotas); $q++) {
+ $parts = explode(',', $quotas[$q]);
+ $messages = array_merge($messages, $this->checkQuota($parts[0], $parts[1], $parts[2], $parts[3], $parts[4], $i));
+ $partialAccounts[$i]['quota'][] = $quotas[$q];
+ }
+ }
+ }
+ return $messages;
+ }
+
+ /**
+ * Returns a list of PDF entries
+ */
+ public function get_pdfEntries() {
+ $return = array();
+ if (isset($this->attributes['quota'][0])) {
+ $quotas[] = '' .
+ '' . _('Mountpoint') . ' | ' .
+ '' . _('Soft block') . ' | ' .
+ '' . _('Hard block') . ' | ' .
+ '' . _('Soft inode') . ' | ' .
+ '' . _('Hard inode') . ' |
';
+ for ($i = 0; $i < sizeof($this->attributes['quota']); $i++) {
+ $parts = explode(',', $this->attributes['quota'][$i]);
+ $quotas[] = '' .
+ '' . $parts[0] . ' | ' .
+ '' . $parts[1] . ' | ' .
+ '' . $parts[2] . ' | ' .
+ '' . $parts[3] . ' | ' .
+ '' . $parts[4] . ' |
';
+ }
+ $return['systemQuotas_quota'] = $quotas;
+ }
+ return $return;
+ }
+
+}
+
+
+?>