Location: PHPKode > projects > Openmailadmin > openmailadmin-1.0.1/inc/lib/openmailadmin.php
<?php
/**
 * This class collects all methods of Openmailadmin, except for the view
 * and data storage.
 */
class openmailadmin
{
	public	$current_user;		// What user do we edit/display currently?
	public	$authenticated_user;	// What user did log in?

	private	$db;
	private $validator;
	protected	$ErrorHandler;

	private	$tablenames;
	private	$cfg;
	public	$imap;

	// "alias" == "local part"
	const	regex_valid_alias	= '(?=^.{1,64}$)[a-z0-9]+(?:(?<![!$+\-_.])[!$+\-_.][a-z0-9]+)*';
	const	regex_valid_email	= '[a-z0-9]+(?:(?<![!$+\-_.])[!$+\-_.][a-z0-9]+)*@(?:(?:(?![.-])[a-z0-9\-]{1,63}(?<!-)\.?)+(?:(?<!\.)\.[a-z]{2,}))';
	const	regex_valid_domain	= '(?=^.{1,254}$)(?:^localhost$)|(?:^(?:(?![.-])[a-z0-9\-]{1,63}(?<!-)\.?)+(?:(?<!\.)\.[a-z]{2,})$)';

	function __construct(ADOConnection $adodb_handler, array $tablenames, array $cfg, IMAP_Administrator $imap) {
		$this->db		= $adodb_handler;
		$this->tablenames	= $tablenames;
		$this->cfg		= $cfg;
		$this->imap		= $imap;
		$this->validator	= new InputValidatorSuite($this, $cfg);
		$this->ErrorHandler	= ErrorHandler::getInstance();
	}

	/*
	 * This procedure simply executes every command stored in the array.
	 */
	private function rollback($what) {
		if(is_array($what)) {
			foreach($what as $cmd) {
				eval($cmd.';');
			}
		} else {
			eval($what.';');
		}
	}

	/*
	 * Returns a long list with every active mailbox.
	 */
	private function get_mailbox_names() {
		$tmp	= array();

		$result = $this->db->Execute('SELECT mbox FROM '.$this->tablenames['user'].' WHERE active = 1');
		while(!$result->EOF) {
			if($result->fields['mbox'] != '')
				$tmp[] = $result->fields['mbox'];
			$result->MoveNext();
		}
		return $tmp;
	}

	/*
	 * As the name says, returns an array containing the entire row
	 * of the "user" table belonging to that mailbox.
	 */
	public function get_user_row($mailbox) {
		return $this->db->GetRow('SELECT * FROM '.$this->tablenames['user'].' WHERE mbox='.$this->db->qstr($mailbox));
	}

	/*
	 * Accepts a string containing possible destination for an email-address,
	 * selects valid destinations and returns them.
	 */
	public function get_valid_destinations($possible) {
		// Define what addresses we will accept.
		$pattern  = openmailadmin::regex_valid_email;
		$pattern .= '|'.$this->current_user->mbox.'|'.txt('5').'|'.strtolower(txt('5'));
		if($this->cfg['allow_mbox_as_target']) {
			$mailboxes = &$this->get_mailbox_names();
			if(count($mailboxes) > 0) {
				$mailboxes = array_map('preg_quote', $mailboxes);
				$pattern .= '|'.implode('|', $mailboxes);
			}
		} else if($this->cfg['allow_wcyr_as_target']) {
			$pattern .= '|[a-z]{2,}[0-9]{4}';
		}

		// Get valid destinations.
		if(preg_match_all('/'.$pattern.'/iu', $possible, $matched)) {
			if(is_array($matched[0])) {
				// Replace every occurence of 'mailbox' with the correct name.
				array_walk($matched[0],
					create_function('&$item,$index',
							'if(strtolower($item) == \''.strtolower(txt('5')).'\') $item = \''.$this->current_user->mbox.'\';'
							));
				return $matched[0];
			}
		}
		return array();
	}

	/*
	 * Returns an array containing all domains the user may choose from.
	 */
	public function get_domain_set($user, $categories, $cache = true) {
		$cat = '';
		$poss_dom = array();

		if($cache && isset($_SESSION['cache']['getDomainSet'][$user][$categories])) {
			return $_SESSION['cache']['getDomainSet'][$user][$categories];
		} else {
			foreach(explode(',', $categories) as $value) {
				$poss_dom[] = trim($value);
				$cat .= ' OR categories LIKE '.$this->db->qstr('%'.trim($value).'%');
			}
			$dom = array();
			$result = $this->db->Execute('SELECT domain FROM '.$this->tablenames['domains']
				.' WHERE owner='.$this->db->qstr($user).' OR a_admin LIKE '.$this->db->qstr('%'.$user.'%').' OR '.db_find_in_set($this->db, 'domain', $poss_dom).$cat);
			if(!$result === false) {
				while(!$result->EOF) {
					$dom[] = idn_to_utf8($result->fields['domain']);
					$result->MoveNext();
				}
			}

			$_SESSION['cache']['getDomainSet'][$user][$categories] = $dom;
			return $_SESSION['cache']['getDomainSet'][$user][$categories];
		}
	}

	/*
	 * Checks whether a user is a descendant of another user.
	 * (Unfortunately, PHP does not support inline functions.)
	 */
	public function user_is_descendant($child, $parent, $levels = 7, $cache = array()) {
		// initialize cache
		if(!isset($_SESSION['cache']['IsDescendant'])) {
			$_SESSION['cache']['IsDescendant'] = array();
		}

		if(trim($child) == '' || trim($parent) == '')
			return false;
		if(isset($_SESSION['cache']['IsDescendant'][$parent][$child]))
			return $_SESSION['cache']['IsDescendant'][$parent][$child];

		if($child == $parent) {
			$rec = true;
		} else if($levels <= 0 ) {
			$rec = false;
		} else {
			$inter = $this->db->GetOne('SELECT pate FROM '.$this->tablenames['user'].' WHERE mbox='.$this->db->qstr($child));
			if($inter === false) {
				$rec = false;
			} else {
				if($inter == $parent) {
					$rec = true;
				} else if(in_array($inter, $cache)) {	// avoids loops
					$rec = false;
				} else {
					$rec = $this->user_is_descendant($inter, $parent, $levels--, array_merge($cache, array($inter)));
				}
			}
		}
		$_SESSION['cache']['IsDescendant'][$parent][$child] = $rec;
		return $rec;
	}

	/*
	 * How many aliases the user has already in use?
	 * Does cache, but not session-wide.
	 */
	public function user_get_used_alias($username) {
		static $used = array();
		if(!isset($used[$username])) {
			$used[$username] = $this->db->GetOne('SELECT COUNT(*) FROM '.$this->tablenames['virtual'].' WHERE owner='.$this->db->qstr($username));
		}
		return $used[$username];
	}
	/*
	 * How many regexp-addresses the user has already in use?
	 * Does cache, but not session-wide.
	 */
	public function user_get_used_regexp($username) {
		static $used = array();
		if(!isset($used[$username])) {
			$used[$username] = $this->db->GetOne('SELECT COUNT(*) FROM '.$this->tablenames['virtual_regexp'].' WHERE owner='.$this->db->qstr($username));
		}
		return $used[$username];
	}

	/*
	 * These just count how many elements have been assigned to that given user.
	 */
	public function user_get_number_mailboxes($username) {
		if(!isset($_SESSION['cache']['n_Mailboxes'][$username]['mailboxes'])) {
			$tmp = $this->db->GetOne('SELECT COUNT(*) FROM '.$this->tablenames['user'].' WHERE pate='.$this->db->qstr($username));
			$_SESSION['cache']['n_Mailboxes'][$username]['mailboxes'] = $tmp;
		}
		return $_SESSION['cache']['n_Mailboxes'][$username]['mailboxes'];
	}
	/*
	 * These just count how many elements have been assigned to that given user.
	 */
	public function user_get_number_domains($username) {
		if(!isset($_SESSION['cache']['n_Domains'][$username]['domains'])) {
			$tmp = $this->db->GetOne('SELECT COUNT(*) FROM '.$this->tablenames['domains'].' WHERE owner='.$this->db->qstr($username));
			$_SESSION['cache']['n_Domains'][$username]['domains'] = $tmp;
		}
		return $_SESSION['cache']['n_Domains'][$username]['domains'];
	}
	/*
	 * In case you have changed something about domains...
	 */
	private function user_invalidate_domain_sets() {
		if(isset($_SESSION['cache']['getDomainSet'])) {
			unset($_SESSION['cache']['getDomainSet']);
		}
	}

/* ******************************* addresses ******************************** */
	/*
	 * Returns a long list with all addresses (the virtuals' table).
	 */
	public function get_addresses() {
		$alias = array();

		$result = $this->db->SelectLimit('SELECT address, dest, active'
					.' FROM '.$this->tablenames['virtual']
					.' WHERE owner='.$this->db->qstr($this->current_user->mbox).$_SESSION['filter']['str']['address']
					.' ORDER BY address, dest',
					$_SESSION['limit'], $_SESSION['offset']['address']);
		if(!$result === false) {
			while(!$result->EOF) {
				$row	= $result->fields;
				// explode all destinations (as there may be many)
				$dest = array();
				foreach(explode(',', $row['dest']) as $value) {
					$value = trim($value);
					// replace the current user's name with "mailbox"
					if($value == $this->current_user->mbox)
						$dest[] = txt('5');
					else
						$dest[] = $value;
				}
				sort($dest);
				$row['dest'] = $dest;
				// detect where the "@" is
				$at = strpos($row['address'], '@');
				//turn the alias of catchalls to a star
				if($at == 0)
					$row['alias'] = '*';
				else
					$row['alias'] = substr($row['address'], 0, $at);
				$row['domain'] = idn_to_utf8(substr($row['address'], $at+1));
				// add the current entry to our list of aliases
				$alias[] = $row;
				$result->MoveNext();
			}
			usort($alias, create_function('$a, $b', 'return ($a["domain"] == $b["domain"] ? strcmp($a["alias"], $b["alias"]) : strcmp($a["domain"], $b["domain"]));'));
		}
		return $alias;
	}

	/*
	 * Creates a new email-address.
	 */
	public function address_create($alias, $domain, $arr_destinations) {
		$domain = idn_to_ascii($domain);
		// May the user create another address?
		if($this->current_user->used_alias < $this->current_user->max_alias
		   || $this->authenticated_user->a_super >= 1) {
			// If he did choose a catchall, may he create such an address?
			if($alias == '*' && $this->cfg['address']['allow_catchall']) {
				if($this->cfg['address']['restrict_catchall']) {
					// If either the current or the authenticated user is
					// owner of that given domain, we can permit creation of that catchall.
					$result = $this->db->GetOne('SELECT domain FROM '.$this->tablenames['domains']
								.' WHERE domain='.$this->db->qstr($domain)
								.' AND (owner='.$this->db->qstr($this->current_user->mbox).' OR owner='.$this->db->qstr($this->authenticated_user->mbox).')');
					if($result === false) {			// negative check!
						$this->ErrorHandler->add_error(txt('16'));
						return false;
					}
					// There shall be no local part in the address. That is characteristic for catchalls.
					$alias = '';
				}
			}
			// Will his new address be a valid one?
			else if(! preg_match('/^'.openmailadmin::regex_valid_alias.'$/i', $alias)) {
				$this->ErrorHandler->add_error(txt('13'));
				return false;
			}
			// Restrict amount of possible destinations.
			$max = $alias == '' ? $this->cfg['address']['max_dest_p_catchall'] : $this->cfg['address']['max_dest_p_address'];
			if(count($arr_destinations) > $max) {
				$this->ErrorHandler->add_error(sprintf(txt('136'), $max));
				return false;
			}
			// Finally, create that address.
			$this->db->Execute('INSERT INTO '.$this->tablenames['virtual'].' (address, dest, owner) VALUES (?, ?, ?)',
						array(strtolower($alias.'@'.$domain), implode(',', $arr_destinations), $this->current_user->mbox));
			if($this->db->Affected_Rows() < 1) {
				$this->ErrorHandler->add_error(txt('133'));
			} else {
				$this->current_user->used_alias++;
				return true;
			}
		} else {
			$this->ErrorHandler->add_error(txt('14'));
		}
		return false;
	}
	/*
	 * Deletes the given addresses if they belong to the current user.
	 */
	public function address_delete($arr_addresses) {
		$this->db->Execute('DELETE FROM '.$this->tablenames['virtual']
				.' WHERE owner='.$this->db->qstr($this->current_user->mbox)
				.' AND '.db_find_in_set($this->db, 'address', $arr_addresses));
		if($this->db->Affected_Rows() < 1) {
			if($this->db->ErrorNo() != 0) {
				$this->ErrorHandler->add_error($this->db->ErrorMsg());
			}
		} else {
			array_walk($arr_addresses,
					create_function('&$item,$index',
							'$parts = explode(\'@\', $item); $item = implode(\'@\', array($parts[0], idn_to_utf8($parts[1])));'
							));
			$this->ErrorHandler->add_info(sprintf(txt('15'), implode(',', $arr_addresses)));
			$this->current_user->used_alias -= $this->db->Affected_Rows();
			return true;
		}

		return false;
	}
	/*
	 * Changes the destination of the given addresses if they belong to the current user.
	 */
	public function address_change_destination($arr_addresses, $arr_destinations) {
		$max = $this->cfg['address']['max_dest_p_address'];
		foreach($arr_addresses as $addr) {
			if($addr{0} == '@') { // catchall!
				$max = $this->cfg['address']['max_dest_p_catchall'];
				break;
			}
		}
		if(count($arr_destinations) > $max) {
			$this->ErrorHandler->add_error(sprintf(txt('136'), $max));
			return false;
		}
		$this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET dest='.$this->db->qstr(implode(',', $arr_destinations)).', neu=1'
				.' WHERE owner='.$this->db->qstr($this->current_user->mbox)
				.' AND '.db_find_in_set($this->db, 'address', $arr_addresses));
		if($this->db->Affected_Rows() < 1) {
			if($this->db->ErrorNo() != 0) {
				$this->ErrorHandler->add_error($this->db->ErrorMsg());
			}
		} else {
			return true;
		}
		return false;
	}
	/*
	 * Toggles the 'active'-flag of a set of addresses  of the current user
	 * and thus sets inactive ones to active ones and vice versa.
	 */
	public function address_toggle_active($arr_addresses) {
		$this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET active=NOT active, neu=1'
				.' WHERE owner='.$this->db->qstr($this->current_user->mbox)
				.' AND '.db_find_in_set($this->db, 'address', $arr_addresses));
		if($this->db->Affected_Rows() < 1) {
			if($this->db->ErrorNo() != 0) {
				$this->ErrorHandler->add_error($this->db->ErrorMsg());
			}
		} else {
			return true;
		}

		return false;
	}

/* ******************************* domains ********************************** */
	public $editable_domains;	// How many domains can the current user change?
	/*
	 * Returns a long list with all domains (from table 'domains').
	 */
	public function get_domains() {
		$this->editable_domains = 0;
		$domains = array();

		$query  = 'SELECT * FROM '.$this->tablenames['domains'];
		if($this->authenticated_user->a_super > 0) {
			$query .= ' WHERE 1=1 '.$_SESSION['filter']['str']['domain'];
		} else {
			$query .= ' WHERE (owner='.$this->db->qstr($this->current_user->mbox).' or a_admin LIKE '.$this->db->qstr('%'.$this->current_user->mbox.'%').')'
				 .$_SESSION['filter']['str']['domain'];
		}
		$query .= ' ORDER BY owner, length(a_admin), domain';

		$result = $this->db->SelectLimit($query, $_SESSION['limit'], $_SESSION['offset']['mbox']);
		if(!$result === false) {
			while(!$result->EOF) {
				$row	= $result->fields;
				if($row['owner'] == $this->authenticated_user->mbox
				   || find_in_set($this->authenticated_user->mbox, $row['a_admin'])) {
					$row['selectable']	= true;
					++$this->editable_domains;
				} else {
					$row['selectable']	= false;
				}
				$row['domain'] = idn_to_utf8($row['domain']);
				$domains[] = $row;
				$result->MoveNext();
			}
		}

		$this->current_user->n_domains = $this->user_get_number_domains($this->current_user->mbox);

		return $domains;
	}
	/**
	 * May the new user only select from domains which have been assigned to
	 * the reference user? If so, return true.
	 *
	 * @param	reference	Instance of User
	 * @param	tobechecked	Mailbox-name.
	 */
	public function domain_check(User $reference, $tobechecked, $domain_key) {
		if(!isset($reference->domain_set)) {
			$reference->domain_set = $this->get_domain_set($reference->mbox, $reference->domains);
		}
		// new domain-key must not lead to more domains than the user already has to choose from
		// A = Domains the new user will be able to choose from.
		$dom_a = $this->get_domain_set($tobechecked, $domain_key, false);
		// B = Domains the creator may choose from (that is $reference['domain_set'])?
		// Okay, if A is part of B. (Thus, no additional domains are added for user "A".)
		// Indication: A <= B
		if(count($dom_a) == 0) {
			// This will be only a warning.
			$this->ErrorHandler->add_error(txt('80'));
		} else if(count($dom_a) > count($reference->domain_set)
			   && count(array_diff($dom_a, $reference->domain_set)) > 0) {
			// A could have domains which the reference cannot access.
			return false;
		}

		return true;
	}
	/*
	 * Adds a new domain into the corresponding table.
	 * Categories are for grouping domains.
	 */
	public function domain_add($domain, $props) {
		$domain = idn_to_ascii($domain);
		$props['domain'] = $domain;
		if(!$this->validator->validate($props, array('domain', 'categories', 'owner', 'a_admin'))) {
			return false;
		}

		if(!stristr($props['categories'], 'all'))
			$props['categories'] = 'all,'.$props['categories'];
		if(!stristr($props['a_admin'], $this->current_user->mbox))
			$props['a_admin'] .= ','.$this->current_user->mbox;

		$this->db->Execute('INSERT INTO '.$this->tablenames['domains'].' (domain, categories, owner, a_admin) VALUES (?, ?, ?, ?)',
				array($domain, $props['categories'], $props['owner'], $props['a_admin']));
		if($this->db->Affected_Rows() < 1) {
			$this->ErrorHandler->add_error(txt('134'));
		} else {
			$this->user_invalidate_domain_sets();
			return true;
		}

		return false;
	}
	/*
	 * Not only removes the given domains by their ids,
	 * it deactivates every address which ends in that domain.
	 */
	public function domain_remove($domains) {
		// We need the old domain name later...
		if(is_array($domains) && count($domains) > 0) {
			if($this->cfg['admins_delete_domains']) {
				$result = $this->db->SelectLimit('SELECT ID, domain'
					.' FROM '.$this->tablenames['domains']
					.' WHERE (owner='.$this->db->qstr($this->authenticated_user->mbox).' OR a_admin LIKE '.$this->db->qstr('%'.$this->authenticated_user->mbox.'%').') AND '.db_find_in_set($this->db, 'ID', $domains),
					count($domains));
			} else {
				$result = $this->db->SelectLimit('SELECT ID, domain'
					.' FROM '.$this->tablenames['domains']
					.' WHERE owner='.$this->db->qstr($this->authenticated_user->mbox).' AND '.db_find_in_set($this->db, 'ID', $domains),
					count($domains));
			}
			if(!$result === false) {
				while(!$result->EOF) {
					$del_ID[] = $result->fields['ID'];
					$del_nm[] = idn_to_utf8($result->fields['domain']);
					$result->MoveNext();
				}
				if(isset($del_ID)) {
					$this->db->Execute('DELETE FROM '.$this->tablenames['domains'].' WHERE '.db_find_in_set($this->db, 'ID', $del_ID));
					if($this->db->Affected_Rows() < 1) {
						if($this->db->ErrorNo() != 0) {
							$this->ErrorHandler->add_error($this->db->ErrorMsg());
						}
					} else {
						$this->ErrorHandler->add_info(txt('52').'<br />'.implode(', ', $del_nm));
						// We better deactivate all aliases containing that domain, so users can see the domain was deleted.
						foreach($del_nm as $domainname) {
							$this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET active = 0, neu = 1 WHERE address LIKE '.$this->db->qstr('%'.idn_to_ascii($domainname)));
						}
						// We can't do such on REGEXP addresses: They may catch more than the given domains.
						$this->user_invalidate_domain_sets();
						return true;
					}
				} else {
					$this->ErrorHandler->add_error(txt('16'));
				}
			} else {
				$this->ErrorHandler->add_error(txt('16'));
			}
		} else {
			$this->ErrorHandler->add_error(txt('11'));
		}

		return false;
	}
	/*
	 * Every parameter is an array. $domains contains IDs.
	 */
	public function domain_change($domains, $change, $data) {
		$toc = array();		// to be changed

		if(!$this->validator->validate($data, $change)) {
			return false;
		}

		if(!is_array($change)) {
			$this->ErrorHandler->add_error(txt('53'));
			return false;
		}
		if($this->cfg['admins_delete_domains'] && in_array('owner', $change))
			$toc[] = 'owner='.$this->db->qstr($data['owner']);
		if(in_array('a_admin', $change))
			$toc[] = 'a_admin='.$this->db->qstr($data['a_admin']);
		if(in_array('categories', $change))
			$toc[] = 'categories='.$this->db->qstr($data['categories']);
		if(count($toc) > 0) {
			$this->db->Execute('UPDATE '.$this->tablenames['domains']
				.' SET '.implode(',', $toc)
				.' WHERE (owner='.$this->db->qstr($this->authenticated_user->mbox).' or a_admin LIKE '.$this->db->qstr('%'.$this->authenticated_user->mbox.'%').') AND '.db_find_in_set($this->db, 'ID', $domains));
			if($this->db->Affected_Rows() < 1) {
				if($this->db->ErrorNo() != 0) {
					$this->ErrorHandler->add_error($this->db->ErrorMsg());
				} else {
					$this->ErrorHandler->add_error(txt('16'));
				}
			}
		}
		// changing ownership if $this->cfg['admins_delete_domains'] == false
		if(!$this->cfg['admins_delete_domains'] && in_array('owner', $change)) {
			$this->db->Execute('UPDATE '.$this->tablenames['domains']
				.' SET owner='.$this->db->qstr($data['owner'])
				.' WHERE owner='.$this->db->qstr($this->authenticated_user->mbox).' AND '.db_find_in_set($this->db, 'ID', $domains));
		}
		$this->user_invalidate_domain_sets();
		// No domain be renamed?
		if(! in_array('domain', $change)) {
			return true;
		}
		// Otherwise (and if only one) try adapting older addresses.
		if(count($domains) == 1) {
			// Grep the old name, we will need it later for replacement.
			$domain = $this->db->GetRow('SELECT ID, domain AS name FROM '.$this->tablenames['domains'].' WHERE ID = '.$this->db->qstr($domains[0]).' AND (owner='.$this->db->qstr($this->authenticated_user->mbox).' or a_admin LIKE '.$this->db->qstr('%'.$this->authenticated_user->mbox.'%').')');
			if(!$domain === false) {
				// First, update the name. (Corresponding field is marked as unique, therefore we will not receive doublettes.)...
				$this->db->Execute('UPDATE '.$this->tablenames['domains'].' SET domain = '.$this->db->qstr($data['domain']).' WHERE ID = '.$domain['ID']);
				// ... and then, change every old address.
				if($this->db->Affected_Rows() == 1) {
					// address
					$this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET neu = 1, address = REPLACE(address, '.$this->db->qstr('@'.$domain['name']).', '.$this->db->qstr('@'.$data['domain']).') WHERE address LIKE '.$this->db->qstr('%@'.$domain['name']));
					// dest
					$this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET neu = 1, dest = REPLACE(dest, '.$this->db->qstr('@'.$domain['name']).', '.$this->db->qstr('@'.$data['domain']).') WHERE dest LIKE '.$this->db->qstr('%@'.$domain['name'].'%'));
					$this->db->Execute('UPDATE '.$this->tablenames['virtual_regexp'].' SET neu = 1, dest = REPLACE(dest, '.$this->db->qstr('@'.$domain['name']).', '.$this->db->qstr('@'.$data['domain']).') WHERE dest LIKE '.$this->db->qstr('%@'.$domain['name'].'%'));
					// canonical
					$this->db->Execute('UPDATE '.$this->tablenames['user'].' SET canonical = REPLACE(canonical, '.$this->db->qstr('@'.$domain['name']).', '.$this->db->qstr('@'.$data['domain']).') WHERE canonical LIKE '.$this->db->qstr('%@'.$domain['name']));
				} else {
					$this->ErrorHandler->add_error($this->db->ErrorMsg());
				}
				return true;
			} else {
				$this->ErrorHandler->add_error(txt('91'));
			}
		} else {
			$this->ErrorHandler->add_error(txt('53'));
		}

		return false;
	}

/* ******************************* passwords ******************************** */
	/**
	 * Changes the current user's password.
	 * This requires the former password for authentication if current user and
	 * authenticated user are the same.
	 */
	public function user_change_password($new, $new_repeat, $old_passwd = null) {
		if($this->current_user->mbox == $this->authenticated_user->mbox
		   && !is_null($old_passwd)
		   && !$this->current_user->password->equals($old_passwd)) {
			$this->ErrorHandler->add_error(txt('45'));
		} else if($new != $new_repeat) {
			$this->ErrorHandler->add_error(txt('44'));
		} else if(strlen($new) < $this->cfg['passwd']['min_length']
			|| strlen($new) > $this->cfg['passwd']['max_length']) {
			$this->ErrorHandler->add_error(sprintf(txt('46'), $this->cfg['passwd']['min_length'], $this->cfg['passwd']['max_length']));
		} else {
			// Warn about insecure passwords, but let them pass.
			if(!Password::is_secure($new)) {
				$this->ErrorHandler->add_error(txt('47'));
			}
			if($this->current_user->password->set($new)) {
				$this->ErrorHandler->add_info(txt('48'));
				return true;
			}
		}
		return false;
	}

/* ******************************* regexp *********************************** */
	/*
	 * Returns a long list with all regular expressions (the virtual_regexp table).
	 * If $match_against is given, the flag "matching" will be set on matches.
	 */
	public function get_regexp($match_against = null) {
		$regexp = array();

		$result = $this->db->SelectLimit('SELECT * FROM '.$this->tablenames['virtual_regexp']
				.' WHERE owner='.$this->db->qstr($this->current_user->mbox).$_SESSION['filter']['str']['regexp']
				.' ORDER BY dest',
				$_SESSION['limit'], $_SESSION['offset']['regexp']);
		if(!$result === false) {
			while(!$result->EOF) {
				$row	= $result->fields;
				// if ordered, check whether expression matches probe address
				if(!is_null($match_against)
				   && @preg_match($row['reg_exp'], $match_against)) {
					$row['matching']	= true;
				} else {
					$row['matching']	= false;
				}
				// explode all destinations (as there may be many)
				$dest = array();
				foreach(explode(',', $row['dest']) as $value) {
					$value = trim($value);
					// replace the current user's name with "mailbox"
					if($value == $this->current_user->mbox)
					$dest[] = txt('5');
					else
					$dest[] = $value;
				}
				sort($dest);
				$row['dest'] = $dest;
				// add the current entry to our list of aliases
				$regexp[] = $row;
				$result->MoveNext();
			}
		}
		return $regexp;
	}
	/*
	 * Creates a new regexp-address.
	 */
	public function regexp_create($regexp, $arr_destinations) {
		// some dull checks;
		// if someone knows how to find out whether an string is a valid regexp -> write me please
		if($regexp == '' || $regexp{0} != '/') {
			$this->ErrorHandler->add_error(txt('127'));
			return false;
		}

		if($this->current_user->used_regexp < $this->current_user->max_regexp
		   || $this->authenticated_user->a_super > 0) {
			$this->db->Execute('INSERT INTO '.$this->tablenames['virtual_regexp'].' (reg_exp, dest, owner) VALUES (?, ?, ?)',
				array($regexp, implode(',', $arr_destinations), $this->current_user->mbox));
			if($this->db->Affected_Rows() < 1) {
				if($this->db->ErrorNo() != 0) {
					$this->ErrorHandler->add_error(txt('133'));
				}
			} else {
				$this->current_user->used_regexp++;
				return true;
			}
		} else {
			$this->ErrorHandler->add_error(txt('31'));
		}

		return false;
	}
	/*
	 * Deletes the given regular expressions if they belong to the current user.
	 */
	public function regexp_delete($arr_regexp_ids) {
		$this->db->Execute('DELETE FROM '.$this->tablenames['virtual_regexp']
				.' WHERE owner='.$this->db->qstr($this->current_user->mbox)
				.' AND '.db_find_in_set($this->db, 'ID', $arr_regexp_ids));
		if($this->db->Affected_Rows() < 1) {
			if($this->db->ErrorNo() != 0) {
				$this->ErrorHandler->add_error($this->db->ErrorMsg());
			}
		} else {
			$this->ErrorHandler->add_info(txt('32'));
			$this->current_user->used_regexp -= $this->db->Affected_Rows();
			return true;
		}

		return false;
	}
	/*
	 * See "address_change_destination".
	 */
	public function regexp_change_destination($arr_regexp_ids, $arr_destinations) {
		$this->db->Execute('UPDATE '.$this->tablenames['virtual_regexp'].' SET dest='.$this->db->qstr(implode(',', $arr_destinations)).', neu = 1'
				.' WHERE owner='.$this->db->qstr($this->current_user->mbox)
				.' AND '.db_find_in_set($this->db, 'ID', $arr_regexp_ids));
		if($this->db->Affected_Rows() < 1) {
			if($this->db->ErrorNo() != 0) {
				$this->ErrorHandler->add_error($this->db->ErrorMsg());
			}
		} else {
			return true;
		}

		return false;
	}
	/*
	 * See "address_toggle_active".
	 */
	public function regexp_toggle_active($arr_regexp_ids) {
		$this->db->Execute('UPDATE '.$this->tablenames['virtual_regexp'].' SET active = NOT active, neu = 1'
				.' WHERE owner='.$this->db->qstr($this->current_user->mbox)
				.' AND '.db_find_in_set($this->db, 'ID', $arr_regexp_ids));
		if($this->db->Affected_Rows() < 1) {
			if($this->db->ErrorNo() != 0) {
				$this->ErrorHandler->add_error($this->db->ErrorMsg());
			}
		} else {
			return true;
		}

		return false;
	}

/* ******************************* mailboxes ******************************** */
	/*
	 * Returns list with mailboxes the current user can see.
	 * Typically all his patenkinder will show up.
	 * If the current user is at his pages and is superuser, he will see all mailboxes.
	 */
	public function get_mailboxes() {
		$mailboxes = array();

		if($this->current_user->mbox == $this->authenticated_user->mbox
		   && $this->authenticated_user->a_super >= 1) {
			$where_clause = ' WHERE TRUE';
		} else {
			$where_clause = ' WHERE pate='.$this->db->qstr($this->current_user->mbox);
		}

		$result = $this->db->SelectLimit('SELECT mbox, person, canonical, pate, max_alias, max_regexp, usr.active, last_login AS lastlogin, a_super, a_admin_domains, a_admin_user, '
						.'COUNT(DISTINCT virt.address) AS num_alias, '
						.'COUNT(DISTINCT rexp.ID) AS num_regexp '
					.'FROM '.$this->tablenames['user'].' usr '
						.'LEFT OUTER JOIN '.$this->tablenames['virtual'].' virt ON (mbox=virt.owner) '
						.'LEFT OUTER JOIN '.$this->tablenames['virtual_regexp'].' rexp ON (mbox=rexp.owner) '
					.$where_clause.$_SESSION['filter']['str']['mbox']
					.' GROUP BY mbox, person, canonical, pate,  max_alias, max_regexp, usr.active, last_login, a_super, a_admin_domains, a_admin_user '
					.'ORDER BY pate, mbox',
					$_SESSION['limit'], $_SESSION['offset']['mbox']);

		if(!$result === false) {
			while(!$result->EOF) {
				if(!in_array($result->fields['mbox'], $this->cfg['user_ignore']))
					$mailboxes[] = $result->fields;
				$result->MoveNext();
			}
		}
		$this->current_user->n_mbox = $this->user_get_number_mailboxes($this->current_user->mbox);

		return $mailboxes;
	}

	/*
	 * This will return a list with $whose's patenkinder for further use in selections.
	 */
	public function get_selectable_paten($whose) {
		if(!isset($_SESSION['paten'][$whose])) {
			$selectable_paten = array();
			if($this->authenticated_user->a_super >= 1) {
				$result = $this->db->Execute('SELECT mbox FROM '.$this->tablenames['user']);
			} else {
				$result = $this->db->Execute('SELECT mbox FROM '.$this->tablenames['user'].' WHERE pate='.$this->db->qstr($whose));
			}
			while(!$result->EOF) {
				if(!in_array($result->fields['mbox'], $this->cfg['user_ignore']))
					$selectable_paten[] = $result->fields['mbox'];
				$result->MoveNext();
			}
			$selectable_paten[] = $whose;
			$selectable_paten[] = $this->authenticated_user->mbox;

			// Array_unique() will do alphabetical sorting.
			$_SESSION['paten'][$whose] = array_unique($selectable_paten);
		}

		return $_SESSION['paten'][$whose];
	}

	/*
	 * Eliminates every mailbox name from $desired_mboxes which is no descendant
	 * of $who. If the authenticated user is superuser, no filtering is done
	 * except elimination imposed by $this->cfg['user_ignore'].
	 */
	private function mailbox_filter_manipulable($who, $desired_mboxes) {
		$allowed = array();

		// Does the authenticated user have the right to do that?
		if($this->authenticated_user->a_super >= 1) {
			$allowed = array_diff($desired_mboxes, $this->cfg['user_ignore']);
		} else {
			foreach($desired_mboxes as $mbox) {
				if(!in_array($mbox, $this->cfg['user_ignore']) && $this->user_is_descendant($mbox, $who)) {
					$allowed[] = $mbox;
				}
			}
		}

		return $allowed;
	}

	/*
	 * $props is typically $_POST, as this function selects all the necessary fields
	 * itself.
	 */
	public function mailbox_create($mboxname, $props) {
		$rollback	= array();

		// Check inputs for sanity and consistency.
		if(!$this->authenticated_user->a_admin_user > 0) {
			$this->ErrorHandler->add_error(txt('16'));
			return false;
		}
		if(in_array($mboxname, $this->cfg['user_ignore'])) {
			$this->ErrorHandler->add_error(sprintf(txt('130'), txt('83')));
			return false;
		}
		if(!$this->validator->validate($props, array('mbox','person','pate','canonical','domains','max_alias','max_regexp','a_admin_domains','a_admin_user','a_super','quota'))) {
			return false;
		}

		// check contingents (only if non-superuser)
		if($this->authenticated_user->a_super == 0) {
			// As the current user's contingents will be decreased we have to use his values.
			if($props['max_alias'] > ($this->current_user->max_alias - $this->user_get_used_alias($this->current_user->mbox))
			   || $props['max_regexp'] > ($this->current_user->max_regexp - $this->user_get_used_regexp($this->current_user->mbox))) {
				$this->ErrorHandler->add_error(txt('66'));
				return false;
			}
			$quota	= $this->imap->get_users_quota($this->current_user->mbox);
			if($quota->is_set && $props['quota']*1024 > $quota->free) {
				$this->ErrorHandler->add_error(txt('65'));
				return false;
			}
		}

		// first create the default-from (canonical) (must not already exist!)
		if($this->cfg['create_canonical']) {
			$this->db->Execute('INSERT INTO '.$this->tablenames['virtual'].' (address, dest, owner) VALUES (?, ?, ?)',
					array($props['canonical'], $mboxname, $mboxname));
			if($this->db->Affected_Rows() < 1) {
				$this->ErrorHandler->add_error(txt('133'));
				return false;
			}
			$rollback[] = '$this->db->Execute(\'DELETE FROM '.$this->tablenames['virtual'].' WHERE address='.addslashes($this->db->qstr($props['canonical'])).' AND owner='.addslashes($this->db->qstr($mboxname)).'\')';
		}

		// on success write the new user to database
		$this->db->Execute('INSERT INTO '.$this->tablenames['user'].' (mbox, person, pate, canonical, domains, max_alias, max_regexp, created, a_admin_domains, a_admin_user, a_super)'
				.' VALUES (?,?,?,?,?,?,?,?,?,?,?)',
				array($props['mbox'], $props['person'], $props['pate'], $props['canonical'], $props['domains'], $props['max_alias'], $props['max_regexp'], time(), $props['a_admin_domains'], $props['a_admin_user'], $props['a_super'])
				);
		if($this->db->Affected_Rows() < 1) {
			$this->ErrorHandler->add_error(txt('92'));
			// Rollback
			$this->rollback($rollback);
			return false;
		}
		$rollback[] = '$this->db->Execute(\'DELETE FROM '.$this->tablenames['user'].' WHERE mbox='.addslashes($this->db->qstr($mboxname)).'\')';

		$tmpu = new User($props['mbox']);
		$pw = $tmpu->password->set_random($this->cfg['passwd']['min_length'], $this->cfg['passwd']['max_length']);

		// Decrease current users's contingents...
		if($this->authenticated_user->a_super == 0) {
			$rollback[] = '$this->db->Execute(\'UPDATE '.$this->tablenames['user'].' SET max_alias='.$this->current_user->max_alias.', max_regexp='.$this->current_user->max_regexp.' WHERE mbox='.addslashes($this->db->qstr($this->current_user->mbox)).'\')';
			$this->db->Execute('UPDATE '.$this->tablenames['user']
				.' SET max_alias='.($this->current_user->max_alias-intval($props['max_alias'])).', max_regexp='.($this->current_user->max_regexp-intval($props['max_regexp']))
				.' WHERE mbox='.$this->db->qstr($this->current_user->mbox));
		}
		// ... and then create the user on the server.
		$result = $this->imap->createmb($this->imap->format_user($mboxname));
		if(!$result) {
			$this->ErrorHandler->add_error($this->imap->error_msg);
			// Rollback
			$this->rollback($rollback);
			return false;
		} else {
			if(isset($this->cfg['folders']['create_default']) && is_array($this->cfg['folders']['create_default'])) {
				foreach($this->cfg['folders']['create_default'] as $new_folder) {
					$this->imap->createmb($this->imap->format_user($mboxname, $new_folder));
				}
			}
		}
		$rollback[] = '$this->imap->deletemb($this->imap->format_user(\''.$mboxname.'\'))';

		// Decrease the creator's quota...
		$cur_usr_quota	= $this->imap->getquota($this->imap->format_user($this->current_user->mbox));
		if($this->authenticated_user->a_super == 0 && $cur_usr_quota->is_set) {
			$result = $this->imap->setquota($this->imap->format_user($this->current_user->mbox), $cur_usr_quota->max - $props['quota']*1024);
			if(!$result) {
				$this->ErrorHandler->add_error($this->imap->error_msg);
				// Rollback
				$this->rollback($rollback);
				return false;
			}
			$rollback[] = '$this->imap->setquota($this->imap->format_user($this->current_user->mbox), '.$cur_usr_quota->max .'))';
			$this->ErrorHandler->add_info(sprintf(txt('69'), floor(($cur_usr_quota->max - $props['quota']*1024)/1024) ));
		} else {
			$this->ErrorHandler->add_info(txt('71'));
		}

		// ... and set the new user's quota.
		if(is_numeric($props['quota'])) {
			$result = $this->imap->setquota($this->imap->format_user($mboxname), $props['quota']*1024);
			if(!$result) {
				$this->ErrorHandler->add_error($this->imap->error_msg);
				// Rollback
				$this->rollback($rollback);
				return false;
			}
		}
		$this->ErrorHandler->add_info(sprintf(txt('72'), $mboxname, $props['person'], $pw));
		if(isset($_SESSION['paten'][$props['pate']])) {
			$_SESSION['paten'][$props['pate']][] = $mboxname;
		}

		return true;
	}

	/*
	 * $props can be $_POST, as every sutable field from $change is used.
	 */
	public function mailbox_change($mboxnames, $change, $props) {
		// Ensure sanity of inputs and check requirements.
		if(!$this->authenticated_user->a_admin_user > 0) {
			$this->ErrorHandler->add_error(txt('16'));
			return false;
		}
		if(!$this->validator->validate($props, $change)) {
			return false;
		}
		$mboxnames = $this->mailbox_filter_manipulable($this->authenticated_user->mbox, $mboxnames);
		if(!count($mboxnames) > 0) {
			return false;
		}

		// Create an array holding every property we have to change.
		$to_change	= array();
		foreach(array('person', 'canonical', 'pate', 'domains', 'a_admin_domains', 'a_admin_user', 'a_super')
			as $property) {
			if(in_array($property, $change)) {
				if(is_numeric($props[$property])) {
					$to_change[]	= $property.' = '.$props[$property];
				} else {
					$to_change[]	= $property.'='.$this->db->qstr($props[$property]);
				}
			}
		}

		// Execute the change operation regarding properties in DB.
		if(count($to_change) > 0) {
			$this->db->Execute('UPDATE '.$this->tablenames['user']
				.' SET '.implode(',', $to_change)
				.' WHERE '.db_find_in_set($this->db, 'mbox', $mboxnames));
		}

		// Manipulate contingents (except quota).
		foreach(array('max_alias', 'max_regexp') as $what) {
			if(in_array($what, $change)) {
				$seek_table = $what == 'max_alias'
						? $this->tablenames['virtual']
						: $this->tablenames['virtual_regexp'];
				$to_be_processed = $mboxnames;
				// Select users which use more aliases than allowed in future.
				$result = $this->db->Execute('SELECT COUNT(*) AS consum, owner, person'
						.' FROM '.$seek_table.','.$this->tablenames['user']
						.' WHERE '.db_find_in_set($this->db, 'owner', $mboxnames).' AND owner=mbox'
						.' GROUP BY owner'
						.' HAVING consum > '.$props[$what]);
				if(!$result === false) {
					// We have to skip them.
					$have_skipped = array();
					while(!$result->EOF) {
						$row	= $result->fields;
						$have_skipped[] = $row['owner'];
						if($this->cfg['mboxview_pers']) {
							$tmp[] = '<a href="'.mkSelfRef(array('cuser' => $row['owner'])).'" title="'.$row['owner'].'">'.$row['person'].' ('.$row['consum'].')</a>';
						} else {
							$tmp[] = '<a href="'.mkSelfRef(array('cuser' => $row['owner'])).'" title="'.$row['person'].'">'.$row['owner'].' ('.$row['consum'].')</a>';
						}
						$result->MoveNext();
					}
					if(count($have_skipped) > 0) {
						$this->ErrorHandler->add_error(sprintf(txt('131'),
									$props[$what], $what == 'max_alias' ? txt('88') : txt('89'),
									implode(', ', $tmp)));
						$to_be_processed = array_diff($to_be_processed, $have_skipped);
					}
				}
				if(count($to_be_processed) > 0) {
					// We don't need further checks if a superuser is logged in.
					if($this->authenticated_user->a_super > 0) {
					$this->db->Execute('UPDATE '.$this->tablenames['user']
						.' SET '.$what.'='.$props[$what]
						.' WHERE '.db_find_in_set($this->db, 'mbox', $to_be_processed));
					} else {
						// Now, calculate whether the current user has enough free contingents.
						$has_to_be_free = $this->db->GetOne('SELECT SUM('.$props[$what].'-'.$what.')'
								.' FROM '.$this->tablenames['user']
								.' WHERE '.db_find_in_set($this->db, 'mbox', $to_be_processed));
						if($has_to_be_free <= $this->user_get_used_alias($this->current_user->mbox)) {
							// If so, set new contingents on the users...
							$this->db->Execute('UPDATE '.$this->tablenames['user']
							.' SET '.$what.'='.$props[$what]
							.' WHERE '.db_find_in_set($this->db, 'mbox', $to_be_processed));
							// ... and add/remove the difference from the current user.
							$this->db->Execute('UPDATE '.$this->tablenames['user']
							.' SET '.$what.'='.$what.'-'.$has_to_be_free
							.' WHERE mbox='.$this->db->qstr($this->current_user->mbox));
						} else {
							// Else, we have to show an error message.
							$this->ErrorHandler->add_error(txt('66'));
						}
					}
				}
			}
		}

		// Change Quota.
		if(in_array('quota', $change)) {
			$add_quota = 0;
			if($this->authenticated_user->a_super == 0) {
				foreach($mboxnames as $user) {
					if($user != '') {
						$quota	= $this->imap->get_users_quota($user);
						if($quota->is_set)
							$add_quota += intval($props['quota'])*1024 - $quota->max;
					}
				}
				$quota	= $this->imap->get_users_quota($this->current_user->mbox);
				if($add_quota != 0 && $quota->is_set) {
					$this->imap->setquota($this->imap->format_user($this->current_user->mbox), $quota->max - $add_quota);
					$this->ErrorHandler->add_info(sprintf(txt('78'), floor(($quota->max - $add_quota)/1024) ));
				}
			}
			reset($mboxnames);
			foreach($mboxnames as $user) {
				if($user != '') {
					$result = $this->imap->setquota($this->imap->format_user($user), intval($props['quota'])*1024);
					if(!$result) {
						$this->ErrorHandler->add_error($this->imap->error_msg);
					}
				}
			}
		}

		// Renaming of (single) user.
		if(in_array('mbox', $change)) {
			if($this->imap->renamemb($this->imap->format_user($mboxnames['0']), $this->imap->format_user($props['mbox']))) {
				$this->db->Execute('UPDATE '.$this->tablenames['user'].' SET mbox='.$this->db->qstr($props['mbox']).' WHERE mbox='.$this->db->qstr($mboxnames['0']));
				$this->db->Execute('UPDATE '.$this->tablenames['domains'].' SET owner='.$this->db->qstr($props['mbox']).' WHERE owner='.$this->db->qstr($mboxnames['0']));
				$this->db->Execute('UPDATE '.$this->tablenames['domains'].' SET a_admin = REPLACE(a_admin, '.$this->db->qstr($mboxnames['0']).', '.$this->db->qstr($props['mbox']).') WHERE a_admin LIKE '.$this->db->qstr('%'.$mboxnames['0'].'%'));
				$this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET dest=REPLACE(dest, '.$this->db->qstr($mboxnames['0']).', '.$this->db->qstr($props['mbox']).'), neu = 1 WHERE dest REGEXP '.$this->db->qstr($mboxnames['0'].'[^@]{1,}').' OR dest LIKE '.$this->db->qstr('%'.$mboxnames['0']));
				$this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET owner='.$this->db->qstr($props['mbox']).' WHERE owner='.$this->db->qstr($mboxnames['0']));
				$this->db->Execute('UPDATE '.$this->tablenames['virtual_regexp'].' SET dest=REPLACE(dest, '.$this->db->qstr($mboxnames['0']).', '.$this->db->qstr($props['mbox']).'), neu = 1 WHERE dest REGEXP '.$this->db->qstr($mboxnames['0'].'[^@]{1,}').' OR dest LIKE '.$this->db->qstr('%'.$mboxnames['0']));
				$this->db->Execute('UPDATE '.$this->tablenames['virtual_regexp'].' SET owner='.$this->db->qstr($props['mbox']).' WHERE owner='.$this->db->qstr($mboxnames['0']));
			} else {
				$this->ErrorHandler->add_error($this->imap->error_msg.'<br />'.txt('94'));
			}
		}

		if(isset($_SESSION['paten']) && in_array(array('mbox', 'pate'), $change)) {
			unset($_SESSION['paten']);	// again: inefficient, but maybe we come up with something more elegant
		}

		return true;
	}

	/*
	 * If ressources are freed, the current user will get them.
	 */
	public function mailbox_delete($mboxnames) {
		$mboxnames = $this->mailbox_filter_manipulable($this->authenticated_user->mbox, $mboxnames);
		if(count($mboxnames) == 0) {
			return false;
		}

		// Delete the given mailboxes from server.
		$add_quota = 0;			// how many space was actually freed?
		$toadd = 0;
		$processed = array();		// which users did we delete successfully?
		foreach($mboxnames as $user) {
			if($user != '') {
				// We have to sum up all the space which gets freed in case we later want increase the deleter's quota.
				$quota	= $this->imap->get_users_quota($user);
				if($this->authenticated_user->a_super == 0
				   && $quota->is_set) {
					$toadd = $quota->max;
				}
				$result = $this->imap->deletemb($this->imap->format_user($user));
				if(!$result) {		// failure
					$this->ErrorHandler->add_error($this->imap->error_msg);
				} else {		// success
					$add_quota += $toadd;
					$processed[] = $user;
				}
			}
		}

		// We need not proceed in case no users were deleted.
		if(count($processed) == 0) {
			return false;
		}

		// Now we have to increase the current user's quota.
		$quota	= $this->imap->get_users_quota($this->current_user->mbox);
		if($this->authenticated_user->a_super == 0
		   && $add_quota > 0
		   && $quota->is_set) {
			$this->imap->setquota($this->imap->format_user($this->current_user->mbox), $quota->max + $add_quota);
			$this->ErrorHandler->add_info(sprintf(txt('76'), floor(($quota->max + $add_quota)/1024) ));
		}

		// Calculate how many contingents get freed if we delete the users.
		if($this->authenticated_user->a_super == 0) {
			$result = $this->db->GetRow('SELECT SUM(max_alias) AS nr_alias, SUM(max_regexp) AS nr_regexp'
					.' FROM '.$this->tablenames['user']
					.' WHERE '.db_find_in_set($this->db, 'mbox', $processed));
			if(!$result === false) {
				$will_be_free = $result;
			}
		}

		// virtual
		$this->db->Execute('DELETE FROM '.$this->tablenames['virtual'].' WHERE '.db_find_in_set($this->db, 'owner', $processed));
		$this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET active=0, neu=1 WHERE '.db_find_in_set($this->db, 'dest', $processed));
		// virtual.regexp
		$this->db->Execute('DELETE FROM '.$this->tablenames['virtual_regexp'].' WHERE '.db_find_in_set($this->db, 'owner', $processed));
		$this->db->Execute('UPDATE '.$this->tablenames['virtual_regexp'].' SET active=0, neu=1 WHERE '.db_find_in_set($this->db, 'dest', $processed));
		// domain (if the one to be deleted owns domains, the deletor will inherit them)
		$this->db->Execute('UPDATE '.$this->tablenames['domains'].' SET owner='.$this->db->qstr($this->current_user->mbox).' WHERE '.db_find_in_set($this->db, 'owner', $processed));
		// user
		$this->db->Execute('DELETE FROM '.$this->tablenames['user'].' WHERE '.db_find_in_set($this->db, 'mbox', $processed));
		if($this->authenticated_user->a_super == 0 && isset($will_be_free['nr_alias'])) {
			$this->db->Execute('UPDATE '.$this->tablenames['user']
			.' SET max_alias='.($this->current_user->max_alias+$will_be_free['nr_alias']).', max_regexp='.($this->current_user->max_regexp+$will_be_free['nr_regexp'])
			.' WHERE mbox='.$this->db->qstr($this->current_user->mbox));
		}
		// patenkinder (will be inherited by the one deleting)
		$this->db->Execute('UPDATE '.$this->tablenames['user']
			.' SET pate='.$this->db->qstr($this->current_user->mbox)
			.' WHERE '.db_find_in_set($this->db, 'pate', $processed));

		$this->ErrorHandler->add_info(sprintf(txt('75'), implode(',', $processed)));
		if(isset($_SESSION['paten'])) unset($_SESSION['paten']); // inefficient, but maybe we come up with something more elegant

		return true;
	}

	/*
	 * active <-> inactive
	 */
	public function mailbox_toggle_active($mboxnames) {
		$tobechanged = $this->mailbox_filter_manipulable($this->current_user->mbox, $mboxnames);
		if(count($tobechanged) > 0) {
			$this->db->Execute('UPDATE '.$this->tablenames['user']
					.' SET active = NOT active'
					.' WHERE '.db_find_in_set($this->db, 'mbox', $tobechanged));
			if($this->db->Affected_Rows() < 1) {
				if($this->db->ErrorNo() != 0) {
					$this->ErrorHandler->add_error($this->db->ErrorMsg());
				}
			} else {
				return true;
			}
		}
		return false;
	}

}
?>
Return current item: Openmailadmin