openmailadmin  1.0.1
About: Openmailadmin is a administration interface to every complete IMAP mail server daemon.
  Fossies Dox: openmailadmin-1.0.1.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

openmailadmin.php
Go to the documentation of this file.
1 <?php
7 {
8  public $current_user; // What user do we edit/display currently?
9  public $authenticated_user; // What user did log in?
10 
11  private $db;
12  private $validator;
13  protected $ErrorHandler;
14 
15  private $tablenames;
16  private $cfg;
17  public $imap;
18 
19  // "alias" == "local part"
20  const regex_valid_alias = '(?=^.{1,64}$)[a-z0-9]+(?:(?<![!$+\-_.])[!$+\-_.][a-z0-9]+)*';
21  const regex_valid_email = '[a-z0-9]+(?:(?<![!$+\-_.])[!$+\-_.][a-z0-9]+)*@(?:(?:(?![.-])[a-z0-9\-]{1,63}(?<!-)\.?)+(?:(?<!\.)\.[a-z]{2,}))';
22  const regex_valid_domain = '(?=^.{1,254}$)(?:^localhost$)|(?:^(?:(?![.-])[a-z0-9\-]{1,63}(?<!-)\.?)+(?:(?<!\.)\.[a-z]{2,})$)';
23 
24  function __construct(ADOConnection $adodb_handler, array $tablenames, array $cfg, IMAP_Administrator $imap) {
25  $this->db = $adodb_handler;
26  $this->tablenames = $tablenames;
27  $this->cfg = $cfg;
28  $this->imap = $imap;
29  $this->validator = new InputValidatorSuite($this, $cfg);
31  }
32 
33  /*
34  * This procedure simply executes every command stored in the array.
35  */
36  private function rollback($what) {
37  if(is_array($what)) {
38  foreach($what as $cmd) {
39  eval($cmd.';');
40  }
41  } else {
42  eval($what.';');
43  }
44  }
45 
46  /*
47  * Returns a long list with every active mailbox.
48  */
49  private function get_mailbox_names() {
50  $tmp = array();
51 
52  $result = $this->db->Execute('SELECT mbox FROM '.$this->tablenames['user'].' WHERE active = 1');
53  while(!$result->EOF) {
54  if($result->fields['mbox'] != '')
55  $tmp[] = $result->fields['mbox'];
56  $result->MoveNext();
57  }
58  return $tmp;
59  }
60 
61  /*
62  * As the name says, returns an array containing the entire row
63  * of the "user" table belonging to that mailbox.
64  */
65  public function get_user_row($mailbox) {
66  return $this->db->GetRow('SELECT * FROM '.$this->tablenames['user'].' WHERE mbox='.$this->db->qstr($mailbox));
67  }
68 
69  /*
70  * Accepts a string containing possible destination for an email-address,
71  * selects valid destinations and returns them.
72  */
73  public function get_valid_destinations($possible) {
74  // Define what addresses we will accept.
76  $pattern .= '|'.$this->current_user->mbox.'|'.txt('5').'|'.strtolower(txt('5'));
77  if($this->cfg['allow_mbox_as_target']) {
78  $mailboxes = &$this->get_mailbox_names();
79  if(count($mailboxes) > 0) {
80  $mailboxes = array_map('preg_quote', $mailboxes);
81  $pattern .= '|'.implode('|', $mailboxes);
82  }
83  } else if($this->cfg['allow_wcyr_as_target']) {
84  $pattern .= '|[a-z]{2,}[0-9]{4}';
85  }
86 
87  // Get valid destinations.
88  if(preg_match_all('/'.$pattern.'/iu', $possible, $matched)) {
89  if(is_array($matched[0])) {
90  // Replace every occurence of 'mailbox' with the correct name.
91  array_walk($matched[0],
92  create_function('&$item,$index',
93  'if(strtolower($item) == \''.strtolower(txt('5')).'\') $item = \''.$this->current_user->mbox.'\';'
94  ));
95  return $matched[0];
96  }
97  }
98  return array();
99  }
100 
101  /*
102  * Returns an array containing all domains the user may choose from.
103  */
104  public function get_domain_set($user, $categories, $cache = true) {
105  $cat = '';
106  $poss_dom = array();
107 
108  if($cache && isset($_SESSION['cache']['getDomainSet'][$user][$categories])) {
109  return $_SESSION['cache']['getDomainSet'][$user][$categories];
110  } else {
111  foreach(explode(',', $categories) as $value) {
112  $poss_dom[] = trim($value);
113  $cat .= ' OR categories LIKE '.$this->db->qstr('%'.trim($value).'%');
114  }
115  $dom = array();
116  $result = $this->db->Execute('SELECT domain FROM '.$this->tablenames['domains']
117  .' 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);
118  if(!$result === false) {
119  while(!$result->EOF) {
120  $dom[] = idn_to_utf8($result->fields['domain']);
121  $result->MoveNext();
122  }
123  }
124 
125  $_SESSION['cache']['getDomainSet'][$user][$categories] = $dom;
126  return $_SESSION['cache']['getDomainSet'][$user][$categories];
127  }
128  }
129 
130  /*
131  * Checks whether a user is a descendant of another user.
132  * (Unfortunately, PHP does not support inline functions.)
133  */
134  public function user_is_descendant($child, $parent, $levels = 7, $cache = array()) {
135  // initialize cache
136  if(!isset($_SESSION['cache']['IsDescendant'])) {
137  $_SESSION['cache']['IsDescendant'] = array();
138  }
139 
140  if(trim($child) == '' || trim($parent) == '')
141  return false;
142  if(isset($_SESSION['cache']['IsDescendant'][$parent][$child]))
143  return $_SESSION['cache']['IsDescendant'][$parent][$child];
144 
145  if($child == $parent) {
146  $rec = true;
147  } else if($levels <= 0 ) {
148  $rec = false;
149  } else {
150  $inter = $this->db->GetOne('SELECT pate FROM '.$this->tablenames['user'].' WHERE mbox='.$this->db->qstr($child));
151  if($inter === false) {
152  $rec = false;
153  } else {
154  if($inter == $parent) {
155  $rec = true;
156  } else if(in_array($inter, $cache)) { // avoids loops
157  $rec = false;
158  } else {
159  $rec = $this->user_is_descendant($inter, $parent, $levels--, array_merge($cache, array($inter)));
160  }
161  }
162  }
163  $_SESSION['cache']['IsDescendant'][$parent][$child] = $rec;
164  return $rec;
165  }
166 
167  /*
168  * How many aliases the user has already in use?
169  * Does cache, but not session-wide.
170  */
171  public function user_get_used_alias($username) {
172  static $used = array();
173  if(!isset($used[$username])) {
174  $used[$username] = $this->db->GetOne('SELECT COUNT(*) FROM '.$this->tablenames['virtual'].' WHERE owner='.$this->db->qstr($username));
175  }
176  return $used[$username];
177  }
178  /*
179  * How many regexp-addresses the user has already in use?
180  * Does cache, but not session-wide.
181  */
182  public function user_get_used_regexp($username) {
183  static $used = array();
184  if(!isset($used[$username])) {
185  $used[$username] = $this->db->GetOne('SELECT COUNT(*) FROM '.$this->tablenames['virtual_regexp'].' WHERE owner='.$this->db->qstr($username));
186  }
187  return $used[$username];
188  }
189 
190  /*
191  * These just count how many elements have been assigned to that given user.
192  */
193  public function user_get_number_mailboxes($username) {
194  if(!isset($_SESSION['cache']['n_Mailboxes'][$username]['mailboxes'])) {
195  $tmp = $this->db->GetOne('SELECT COUNT(*) FROM '.$this->tablenames['user'].' WHERE pate='.$this->db->qstr($username));
196  $_SESSION['cache']['n_Mailboxes'][$username]['mailboxes'] = $tmp;
197  }
198  return $_SESSION['cache']['n_Mailboxes'][$username]['mailboxes'];
199  }
200  /*
201  * These just count how many elements have been assigned to that given user.
202  */
203  public function user_get_number_domains($username) {
204  if(!isset($_SESSION['cache']['n_Domains'][$username]['domains'])) {
205  $tmp = $this->db->GetOne('SELECT COUNT(*) FROM '.$this->tablenames['domains'].' WHERE owner='.$this->db->qstr($username));
206  $_SESSION['cache']['n_Domains'][$username]['domains'] = $tmp;
207  }
208  return $_SESSION['cache']['n_Domains'][$username]['domains'];
209  }
210  /*
211  * In case you have changed something about domains...
212  */
213  private function user_invalidate_domain_sets() {
214  if(isset($_SESSION['cache']['getDomainSet'])) {
215  unset($_SESSION['cache']['getDomainSet']);
216  }
217  }
218 
219 /* ******************************* addresses ******************************** */
220  /*
221  * Returns a long list with all addresses (the virtuals' table).
222  */
223  public function get_addresses() {
224  $alias = array();
225 
226  $result = $this->db->SelectLimit('SELECT address, dest, active'
227  .' FROM '.$this->tablenames['virtual']
228  .' WHERE owner='.$this->db->qstr($this->current_user->mbox).$_SESSION['filter']['str']['address']
229  .' ORDER BY address, dest',
230  $_SESSION['limit'], $_SESSION['offset']['address']);
231  if(!$result === false) {
232  while(!$result->EOF) {
233  $row = $result->fields;
234  // explode all destinations (as there may be many)
235  $dest = array();
236  foreach(explode(',', $row['dest']) as $value) {
237  $value = trim($value);
238  // replace the current user's name with "mailbox"
239  if($value == $this->current_user->mbox)
240  $dest[] = txt('5');
241  else
242  $dest[] = $value;
243  }
244  sort($dest);
245  $row['dest'] = $dest;
246  // detect where the "@" is
247  $at = strpos($row['address'], '@');
248  //turn the alias of catchalls to a star
249  if($at == 0)
250  $row['alias'] = '*';
251  else
252  $row['alias'] = substr($row['address'], 0, $at);
253  $row['domain'] = idn_to_utf8(substr($row['address'], $at+1));
254  // add the current entry to our list of aliases
255  $alias[] = $row;
256  $result->MoveNext();
257  }
258  usort($alias, create_function('$a, $b', 'return ($a["domain"] == $b["domain"] ? strcmp($a["alias"], $b["alias"]) : strcmp($a["domain"], $b["domain"]));'));
259  }
260  return $alias;
261  }
262 
263  /*
264  * Creates a new email-address.
265  */
266  public function address_create($alias, $domain, $arr_destinations) {
267  $domain = idn_to_ascii($domain);
268  // May the user create another address?
269  if($this->current_user->used_alias < $this->current_user->max_alias
270  || $this->authenticated_user->a_super >= 1) {
271  // If he did choose a catchall, may he create such an address?
272  if($alias == '*' && $this->cfg['address']['allow_catchall']) {
273  if($this->cfg['address']['restrict_catchall']) {
274  // If either the current or the authenticated user is
275  // owner of that given domain, we can permit creation of that catchall.
276  $result = $this->db->GetOne('SELECT domain FROM '.$this->tablenames['domains']
277  .' WHERE domain='.$this->db->qstr($domain)
278  .' AND (owner='.$this->db->qstr($this->current_user->mbox).' OR owner='.$this->db->qstr($this->authenticated_user->mbox).')');
279  if($result === false) { // negative check!
280  $this->ErrorHandler->add_error(txt('16'));
281  return false;
282  }
283  // There shall be no local part in the address. That is characteristic for catchalls.
284  $alias = '';
285  }
286  }
287  // Will his new address be a valid one?
288  else if(! preg_match('/^'.openmailadmin::regex_valid_alias.'$/i', $alias)) {
289  $this->ErrorHandler->add_error(txt('13'));
290  return false;
291  }
292  // Restrict amount of possible destinations.
293  $max = $alias == '' ? $this->cfg['address']['max_dest_p_catchall'] : $this->cfg['address']['max_dest_p_address'];
294  if(count($arr_destinations) > $max) {
295  $this->ErrorHandler->add_error(sprintf(txt('136'), $max));
296  return false;
297  }
298  // Finally, create that address.
299  $this->db->Execute('INSERT INTO '.$this->tablenames['virtual'].' (address, dest, owner) VALUES (?, ?, ?)',
300  array(strtolower($alias.'@'.$domain), implode(',', $arr_destinations), $this->current_user->mbox));
301  if($this->db->Affected_Rows() < 1) {
302  $this->ErrorHandler->add_error(txt('133'));
303  } else {
304  $this->current_user->used_alias++;
305  return true;
306  }
307  } else {
308  $this->ErrorHandler->add_error(txt('14'));
309  }
310  return false;
311  }
312  /*
313  * Deletes the given addresses if they belong to the current user.
314  */
315  public function address_delete($arr_addresses) {
316  $this->db->Execute('DELETE FROM '.$this->tablenames['virtual']
317  .' WHERE owner='.$this->db->qstr($this->current_user->mbox)
318  .' AND '.db_find_in_set($this->db, 'address', $arr_addresses));
319  if($this->db->Affected_Rows() < 1) {
320  if($this->db->ErrorNo() != 0) {
321  $this->ErrorHandler->add_error($this->db->ErrorMsg());
322  }
323  } else {
324  array_walk($arr_addresses,
325  create_function('&$item,$index',
326  '$parts = explode(\'@\', $item); $item = implode(\'@\', array($parts[0], idn_to_utf8($parts[1])));'
327  ));
328  $this->ErrorHandler->add_info(sprintf(txt('15'), implode(',', $arr_addresses)));
329  $this->current_user->used_alias -= $this->db->Affected_Rows();
330  return true;
331  }
332 
333  return false;
334  }
335  /*
336  * Changes the destination of the given addresses if they belong to the current user.
337  */
338  public function address_change_destination($arr_addresses, $arr_destinations) {
339  $max = $this->cfg['address']['max_dest_p_address'];
340  foreach($arr_addresses as $addr) {
341  if($addr{0} == '@') { // catchall!
342  $max = $this->cfg['address']['max_dest_p_catchall'];
343  break;
344  }
345  }
346  if(count($arr_destinations) > $max) {
347  $this->ErrorHandler->add_error(sprintf(txt('136'), $max));
348  return false;
349  }
350  $this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET dest='.$this->db->qstr(implode(',', $arr_destinations)).', neu=1'
351  .' WHERE owner='.$this->db->qstr($this->current_user->mbox)
352  .' AND '.db_find_in_set($this->db, 'address', $arr_addresses));
353  if($this->db->Affected_Rows() < 1) {
354  if($this->db->ErrorNo() != 0) {
355  $this->ErrorHandler->add_error($this->db->ErrorMsg());
356  }
357  } else {
358  return true;
359  }
360  return false;
361  }
362  /*
363  * Toggles the 'active'-flag of a set of addresses of the current user
364  * and thus sets inactive ones to active ones and vice versa.
365  */
366  public function address_toggle_active($arr_addresses) {
367  $this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET active=NOT active, neu=1'
368  .' WHERE owner='.$this->db->qstr($this->current_user->mbox)
369  .' AND '.db_find_in_set($this->db, 'address', $arr_addresses));
370  if($this->db->Affected_Rows() < 1) {
371  if($this->db->ErrorNo() != 0) {
372  $this->ErrorHandler->add_error($this->db->ErrorMsg());
373  }
374  } else {
375  return true;
376  }
377 
378  return false;
379  }
380 
381 /* ******************************* domains ********************************** */
382  public $editable_domains; // How many domains can the current user change?
383  /*
384  * Returns a long list with all domains (from table 'domains').
385  */
386  public function get_domains() {
387  $this->editable_domains = 0;
388  $domains = array();
389 
390  $query = 'SELECT * FROM '.$this->tablenames['domains'];
391  if($this->authenticated_user->a_super > 0) {
392  $query .= ' WHERE 1=1 '.$_SESSION['filter']['str']['domain'];
393  } else {
394  $query .= ' WHERE (owner='.$this->db->qstr($this->current_user->mbox).' or a_admin LIKE '.$this->db->qstr('%'.$this->current_user->mbox.'%').')'
395  .$_SESSION['filter']['str']['domain'];
396  }
397  $query .= ' ORDER BY owner, length(a_admin), domain';
398 
399  $result = $this->db->SelectLimit($query, $_SESSION['limit'], $_SESSION['offset']['mbox']);
400  if(!$result === false) {
401  while(!$result->EOF) {
402  $row = $result->fields;
403  if($row['owner'] == $this->authenticated_user->mbox
404  || find_in_set($this->authenticated_user->mbox, $row['a_admin'])) {
405  $row['selectable'] = true;
407  } else {
408  $row['selectable'] = false;
409  }
410  $row['domain'] = idn_to_utf8($row['domain']);
411  $domains[] = $row;
412  $result->MoveNext();
413  }
414  }
415 
416  $this->current_user->n_domains = $this->user_get_number_domains($this->current_user->mbox);
417 
418  return $domains;
419  }
427  public function domain_check(User $reference, $tobechecked, $domain_key) {
428  if(!isset($reference->domain_set)) {
429  $reference->domain_set = $this->get_domain_set($reference->mbox, $reference->domains);
430  }
431  // new domain-key must not lead to more domains than the user already has to choose from
432  // A = Domains the new user will be able to choose from.
433  $dom_a = $this->get_domain_set($tobechecked, $domain_key, false);
434  // B = Domains the creator may choose from (that is $reference['domain_set'])?
435  // Okay, if A is part of B. (Thus, no additional domains are added for user "A".)
436  // Indication: A <= B
437  if(count($dom_a) == 0) {
438  // This will be only a warning.
439  $this->ErrorHandler->add_error(txt('80'));
440  } else if(count($dom_a) > count($reference->domain_set)
441  && count(array_diff($dom_a, $reference->domain_set)) > 0) {
442  // A could have domains which the reference cannot access.
443  return false;
444  }
445 
446  return true;
447  }
448  /*
449  * Adds a new domain into the corresponding table.
450  * Categories are for grouping domains.
451  */
452  public function domain_add($domain, $props) {
453  $domain = idn_to_ascii($domain);
454  $props['domain'] = $domain;
455  if(!$this->validator->validate($props, array('domain', 'categories', 'owner', 'a_admin'))) {
456  return false;
457  }
458 
459  if(!stristr($props['categories'], 'all'))
460  $props['categories'] = 'all,'.$props['categories'];
461  if(!stristr($props['a_admin'], $this->current_user->mbox))
462  $props['a_admin'] .= ','.$this->current_user->mbox;
463 
464  $this->db->Execute('INSERT INTO '.$this->tablenames['domains'].' (domain, categories, owner, a_admin) VALUES (?, ?, ?, ?)',
465  array($domain, $props['categories'], $props['owner'], $props['a_admin']));
466  if($this->db->Affected_Rows() < 1) {
467  $this->ErrorHandler->add_error(txt('134'));
468  } else {
470  return true;
471  }
472 
473  return false;
474  }
475  /*
476  * Not only removes the given domains by their ids,
477  * it deactivates every address which ends in that domain.
478  */
479  public function domain_remove($domains) {
480  // We need the old domain name later...
481  if(is_array($domains) && count($domains) > 0) {
482  if($this->cfg['admins_delete_domains']) {
483  $result = $this->db->SelectLimit('SELECT ID, domain'
484  .' FROM '.$this->tablenames['domains']
485  .' 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),
486  count($domains));
487  } else {
488  $result = $this->db->SelectLimit('SELECT ID, domain'
489  .' FROM '.$this->tablenames['domains']
490  .' WHERE owner='.$this->db->qstr($this->authenticated_user->mbox).' AND '.db_find_in_set($this->db, 'ID', $domains),
491  count($domains));
492  }
493  if(!$result === false) {
494  while(!$result->EOF) {
495  $del_ID[] = $result->fields['ID'];
496  $del_nm[] = idn_to_utf8($result->fields['domain']);
497  $result->MoveNext();
498  }
499  if(isset($del_ID)) {
500  $this->db->Execute('DELETE FROM '.$this->tablenames['domains'].' WHERE '.db_find_in_set($this->db, 'ID', $del_ID));
501  if($this->db->Affected_Rows() < 1) {
502  if($this->db->ErrorNo() != 0) {
503  $this->ErrorHandler->add_error($this->db->ErrorMsg());
504  }
505  } else {
506  $this->ErrorHandler->add_info(txt('52').'<br />'.implode(', ', $del_nm));
507  // We better deactivate all aliases containing that domain, so users can see the domain was deleted.
508  foreach($del_nm as $domainname) {
509  $this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET active = 0, neu = 1 WHERE address LIKE '.$this->db->qstr('%'.idn_to_ascii($domainname)));
510  }
511  // We can't do such on REGEXP addresses: They may catch more than the given domains.
513  return true;
514  }
515  } else {
516  $this->ErrorHandler->add_error(txt('16'));
517  }
518  } else {
519  $this->ErrorHandler->add_error(txt('16'));
520  }
521  } else {
522  $this->ErrorHandler->add_error(txt('11'));
523  }
524 
525  return false;
526  }
527  /*
528  * Every parameter is an array. $domains contains IDs.
529  */
530  public function domain_change($domains, $change, $data) {
531  $toc = array(); // to be changed
532 
533  if(!$this->validator->validate($data, $change)) {
534  return false;
535  }
536 
537  if(!is_array($change)) {
538  $this->ErrorHandler->add_error(txt('53'));
539  return false;
540  }
541  if($this->cfg['admins_delete_domains'] && in_array('owner', $change))
542  $toc[] = 'owner='.$this->db->qstr($data['owner']);
543  if(in_array('a_admin', $change))
544  $toc[] = 'a_admin='.$this->db->qstr($data['a_admin']);
545  if(in_array('categories', $change))
546  $toc[] = 'categories='.$this->db->qstr($data['categories']);
547  if(count($toc) > 0) {
548  $this->db->Execute('UPDATE '.$this->tablenames['domains']
549  .' SET '.implode(',', $toc)
550  .' 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));
551  if($this->db->Affected_Rows() < 1) {
552  if($this->db->ErrorNo() != 0) {
553  $this->ErrorHandler->add_error($this->db->ErrorMsg());
554  } else {
555  $this->ErrorHandler->add_error(txt('16'));
556  }
557  }
558  }
559  // changing ownership if $this->cfg['admins_delete_domains'] == false
560  if(!$this->cfg['admins_delete_domains'] && in_array('owner', $change)) {
561  $this->db->Execute('UPDATE '.$this->tablenames['domains']
562  .' SET owner='.$this->db->qstr($data['owner'])
563  .' WHERE owner='.$this->db->qstr($this->authenticated_user->mbox).' AND '.db_find_in_set($this->db, 'ID', $domains));
564  }
566  // No domain be renamed?
567  if(! in_array('domain', $change)) {
568  return true;
569  }
570  // Otherwise (and if only one) try adapting older addresses.
571  if(count($domains) == 1) {
572  // Grep the old name, we will need it later for replacement.
573  $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.'%').')');
574  if(!$domain === false) {
575  // First, update the name. (Corresponding field is marked as unique, therefore we will not receive doublettes.)...
576  $this->db->Execute('UPDATE '.$this->tablenames['domains'].' SET domain = '.$this->db->qstr($data['domain']).' WHERE ID = '.$domain['ID']);
577  // ... and then, change every old address.
578  if($this->db->Affected_Rows() == 1) {
579  // address
580  $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']));
581  // dest
582  $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'].'%'));
583  $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'].'%'));
584  // canonical
585  $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']));
586  } else {
587  $this->ErrorHandler->add_error($this->db->ErrorMsg());
588  }
589  return true;
590  } else {
591  $this->ErrorHandler->add_error(txt('91'));
592  }
593  } else {
594  $this->ErrorHandler->add_error(txt('53'));
595  }
596 
597  return false;
598  }
599 
600 /* ******************************* passwords ******************************** */
606  public function user_change_password($new, $new_repeat, $old_passwd = null) {
607  if($this->current_user->mbox == $this->authenticated_user->mbox
608  && !is_null($old_passwd)
609  && !$this->current_user->password->equals($old_passwd)) {
610  $this->ErrorHandler->add_error(txt('45'));
611  } else if($new != $new_repeat) {
612  $this->ErrorHandler->add_error(txt('44'));
613  } else if(strlen($new) < $this->cfg['passwd']['min_length']
614  || strlen($new) > $this->cfg['passwd']['max_length']) {
615  $this->ErrorHandler->add_error(sprintf(txt('46'), $this->cfg['passwd']['min_length'], $this->cfg['passwd']['max_length']));
616  } else {
617  // Warn about insecure passwords, but let them pass.
618  if(!Password::is_secure($new)) {
619  $this->ErrorHandler->add_error(txt('47'));
620  }
621  if($this->current_user->password->set($new)) {
622  $this->ErrorHandler->add_info(txt('48'));
623  return true;
624  }
625  }
626  return false;
627  }
628 
629 /* ******************************* regexp *********************************** */
630  /*
631  * Returns a long list with all regular expressions (the virtual_regexp table).
632  * If $match_against is given, the flag "matching" will be set on matches.
633  */
634  public function get_regexp($match_against = null) {
635  $regexp = array();
636 
637  $result = $this->db->SelectLimit('SELECT * FROM '.$this->tablenames['virtual_regexp']
638  .' WHERE owner='.$this->db->qstr($this->current_user->mbox).$_SESSION['filter']['str']['regexp']
639  .' ORDER BY dest',
640  $_SESSION['limit'], $_SESSION['offset']['regexp']);
641  if(!$result === false) {
642  while(!$result->EOF) {
643  $row = $result->fields;
644  // if ordered, check whether expression matches probe address
645  if(!is_null($match_against)
646  && @preg_match($row['reg_exp'], $match_against)) {
647  $row['matching'] = true;
648  } else {
649  $row['matching'] = false;
650  }
651  // explode all destinations (as there may be many)
652  $dest = array();
653  foreach(explode(',', $row['dest']) as $value) {
654  $value = trim($value);
655  // replace the current user's name with "mailbox"
656  if($value == $this->current_user->mbox)
657  $dest[] = txt('5');
658  else
659  $dest[] = $value;
660  }
661  sort($dest);
662  $row['dest'] = $dest;
663  // add the current entry to our list of aliases
664  $regexp[] = $row;
665  $result->MoveNext();
666  }
667  }
668  return $regexp;
669  }
670  /*
671  * Creates a new regexp-address.
672  */
673  public function regexp_create($regexp, $arr_destinations) {
674  // some dull checks;
675  // if someone knows how to find out whether an string is a valid regexp -> write me please
676  if($regexp == '' || $regexp{0} != '/') {
677  $this->ErrorHandler->add_error(txt('127'));
678  return false;
679  }
680 
681  if($this->current_user->used_regexp < $this->current_user->max_regexp
682  || $this->authenticated_user->a_super > 0) {
683  $this->db->Execute('INSERT INTO '.$this->tablenames['virtual_regexp'].' (reg_exp, dest, owner) VALUES (?, ?, ?)',
684  array($regexp, implode(',', $arr_destinations), $this->current_user->mbox));
685  if($this->db->Affected_Rows() < 1) {
686  if($this->db->ErrorNo() != 0) {
687  $this->ErrorHandler->add_error(txt('133'));
688  }
689  } else {
690  $this->current_user->used_regexp++;
691  return true;
692  }
693  } else {
694  $this->ErrorHandler->add_error(txt('31'));
695  }
696 
697  return false;
698  }
699  /*
700  * Deletes the given regular expressions if they belong to the current user.
701  */
702  public function regexp_delete($arr_regexp_ids) {
703  $this->db->Execute('DELETE FROM '.$this->tablenames['virtual_regexp']
704  .' WHERE owner='.$this->db->qstr($this->current_user->mbox)
705  .' AND '.db_find_in_set($this->db, 'ID', $arr_regexp_ids));
706  if($this->db->Affected_Rows() < 1) {
707  if($this->db->ErrorNo() != 0) {
708  $this->ErrorHandler->add_error($this->db->ErrorMsg());
709  }
710  } else {
711  $this->ErrorHandler->add_info(txt('32'));
712  $this->current_user->used_regexp -= $this->db->Affected_Rows();
713  return true;
714  }
715 
716  return false;
717  }
718  /*
719  * See "address_change_destination".
720  */
721  public function regexp_change_destination($arr_regexp_ids, $arr_destinations) {
722  $this->db->Execute('UPDATE '.$this->tablenames['virtual_regexp'].' SET dest='.$this->db->qstr(implode(',', $arr_destinations)).', neu = 1'
723  .' WHERE owner='.$this->db->qstr($this->current_user->mbox)
724  .' AND '.db_find_in_set($this->db, 'ID', $arr_regexp_ids));
725  if($this->db->Affected_Rows() < 1) {
726  if($this->db->ErrorNo() != 0) {
727  $this->ErrorHandler->add_error($this->db->ErrorMsg());
728  }
729  } else {
730  return true;
731  }
732 
733  return false;
734  }
735  /*
736  * See "address_toggle_active".
737  */
738  public function regexp_toggle_active($arr_regexp_ids) {
739  $this->db->Execute('UPDATE '.$this->tablenames['virtual_regexp'].' SET active = NOT active, neu = 1'
740  .' WHERE owner='.$this->db->qstr($this->current_user->mbox)
741  .' AND '.db_find_in_set($this->db, 'ID', $arr_regexp_ids));
742  if($this->db->Affected_Rows() < 1) {
743  if($this->db->ErrorNo() != 0) {
744  $this->ErrorHandler->add_error($this->db->ErrorMsg());
745  }
746  } else {
747  return true;
748  }
749 
750  return false;
751  }
752 
753 /* ******************************* mailboxes ******************************** */
754  /*
755  * Returns list with mailboxes the current user can see.
756  * Typically all his patenkinder will show up.
757  * If the current user is at his pages and is superuser, he will see all mailboxes.
758  */
759  public function get_mailboxes() {
760  $mailboxes = array();
761 
762  if($this->current_user->mbox == $this->authenticated_user->mbox
763  && $this->authenticated_user->a_super >= 1) {
764  $where_clause = ' WHERE TRUE';
765  } else {
766  $where_clause = ' WHERE pate='.$this->db->qstr($this->current_user->mbox);
767  }
768 
769  $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, '
770  .'COUNT(DISTINCT virt.address) AS num_alias, '
771  .'COUNT(DISTINCT rexp.ID) AS num_regexp '
772  .'FROM '.$this->tablenames['user'].' usr '
773  .'LEFT OUTER JOIN '.$this->tablenames['virtual'].' virt ON (mbox=virt.owner) '
774  .'LEFT OUTER JOIN '.$this->tablenames['virtual_regexp'].' rexp ON (mbox=rexp.owner) '
775  .$where_clause.$_SESSION['filter']['str']['mbox']
776  .' GROUP BY mbox, person, canonical, pate, max_alias, max_regexp, usr.active, last_login, a_super, a_admin_domains, a_admin_user '
777  .'ORDER BY pate, mbox',
778  $_SESSION['limit'], $_SESSION['offset']['mbox']);
779 
780  if(!$result === false) {
781  while(!$result->EOF) {
782  if(!in_array($result->fields['mbox'], $this->cfg['user_ignore']))
783  $mailboxes[] = $result->fields;
784  $result->MoveNext();
785  }
786  }
787  $this->current_user->n_mbox = $this->user_get_number_mailboxes($this->current_user->mbox);
788 
789  return $mailboxes;
790  }
791 
792  /*
793  * This will return a list with $whose's patenkinder for further use in selections.
794  */
795  public function get_selectable_paten($whose) {
796  if(!isset($_SESSION['paten'][$whose])) {
797  $selectable_paten = array();
798  if($this->authenticated_user->a_super >= 1) {
799  $result = $this->db->Execute('SELECT mbox FROM '.$this->tablenames['user']);
800  } else {
801  $result = $this->db->Execute('SELECT mbox FROM '.$this->tablenames['user'].' WHERE pate='.$this->db->qstr($whose));
802  }
803  while(!$result->EOF) {
804  if(!in_array($result->fields['mbox'], $this->cfg['user_ignore']))
805  $selectable_paten[] = $result->fields['mbox'];
806  $result->MoveNext();
807  }
808  $selectable_paten[] = $whose;
809  $selectable_paten[] = $this->authenticated_user->mbox;
810 
811  // Array_unique() will do alphabetical sorting.
812  $_SESSION['paten'][$whose] = array_unique($selectable_paten);
813  }
814 
815  return $_SESSION['paten'][$whose];
816  }
817 
818  /*
819  * Eliminates every mailbox name from $desired_mboxes which is no descendant
820  * of $who. If the authenticated user is superuser, no filtering is done
821  * except elimination imposed by $this->cfg['user_ignore'].
822  */
823  private function mailbox_filter_manipulable($who, $desired_mboxes) {
824  $allowed = array();
825 
826  // Does the authenticated user have the right to do that?
827  if($this->authenticated_user->a_super >= 1) {
828  $allowed = array_diff($desired_mboxes, $this->cfg['user_ignore']);
829  } else {
830  foreach($desired_mboxes as $mbox) {
831  if(!in_array($mbox, $this->cfg['user_ignore']) && $this->user_is_descendant($mbox, $who)) {
832  $allowed[] = $mbox;
833  }
834  }
835  }
836 
837  return $allowed;
838  }
839 
840  /*
841  * $props is typically $_POST, as this function selects all the necessary fields
842  * itself.
843  */
844  public function mailbox_create($mboxname, $props) {
845  $rollback = array();
846 
847  // Check inputs for sanity and consistency.
848  if(!$this->authenticated_user->a_admin_user > 0) {
849  $this->ErrorHandler->add_error(txt('16'));
850  return false;
851  }
852  if(in_array($mboxname, $this->cfg['user_ignore'])) {
853  $this->ErrorHandler->add_error(sprintf(txt('130'), txt('83')));
854  return false;
855  }
856  if(!$this->validator->validate($props, array('mbox','person','pate','canonical','domains','max_alias','max_regexp','a_admin_domains','a_admin_user','a_super','quota'))) {
857  return false;
858  }
859 
860  // check contingents (only if non-superuser)
861  if($this->authenticated_user->a_super == 0) {
862  // As the current user's contingents will be decreased we have to use his values.
863  if($props['max_alias'] > ($this->current_user->max_alias - $this->user_get_used_alias($this->current_user->mbox))
864  || $props['max_regexp'] > ($this->current_user->max_regexp - $this->user_get_used_regexp($this->current_user->mbox))) {
865  $this->ErrorHandler->add_error(txt('66'));
866  return false;
867  }
868  $quota = $this->imap->get_users_quota($this->current_user->mbox);
869  if($quota->is_set && $props['quota']*1024 > $quota->free) {
870  $this->ErrorHandler->add_error(txt('65'));
871  return false;
872  }
873  }
874 
875  // first create the default-from (canonical) (must not already exist!)
876  if($this->cfg['create_canonical']) {
877  $this->db->Execute('INSERT INTO '.$this->tablenames['virtual'].' (address, dest, owner) VALUES (?, ?, ?)',
878  array($props['canonical'], $mboxname, $mboxname));
879  if($this->db->Affected_Rows() < 1) {
880  $this->ErrorHandler->add_error(txt('133'));
881  return false;
882  }
883  $rollback[] = '$this->db->Execute(\'DELETE FROM '.$this->tablenames['virtual'].' WHERE address='.addslashes($this->db->qstr($props['canonical'])).' AND owner='.addslashes($this->db->qstr($mboxname)).'\')';
884  }
885 
886  // on success write the new user to database
887  $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)'
888  .' VALUES (?,?,?,?,?,?,?,?,?,?,?)',
889  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'])
890  );
891  if($this->db->Affected_Rows() < 1) {
892  $this->ErrorHandler->add_error(txt('92'));
893  // Rollback
894  $this->rollback($rollback);
895  return false;
896  }
897  $rollback[] = '$this->db->Execute(\'DELETE FROM '.$this->tablenames['user'].' WHERE mbox='.addslashes($this->db->qstr($mboxname)).'\')';
898 
899  $tmpu = new User($props['mbox']);
900  $pw = $tmpu->password->set_random($this->cfg['passwd']['min_length'], $this->cfg['passwd']['max_length']);
901 
902  // Decrease current users's contingents...
903  if($this->authenticated_user->a_super == 0) {
904  $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)).'\')';
905  $this->db->Execute('UPDATE '.$this->tablenames['user']
906  .' SET max_alias='.($this->current_user->max_alias-intval($props['max_alias'])).', max_regexp='.($this->current_user->max_regexp-intval($props['max_regexp']))
907  .' WHERE mbox='.$this->db->qstr($this->current_user->mbox));
908  }
909  // ... and then create the user on the server.
910  $result = $this->imap->createmb($this->imap->format_user($mboxname));
911  if(!$result) {
912  $this->ErrorHandler->add_error($this->imap->error_msg);
913  // Rollback
914  $this->rollback($rollback);
915  return false;
916  } else {
917  if(isset($this->cfg['folders']['create_default']) && is_array($this->cfg['folders']['create_default'])) {
918  foreach($this->cfg['folders']['create_default'] as $new_folder) {
919  $this->imap->createmb($this->imap->format_user($mboxname, $new_folder));
920  }
921  }
922  }
923  $rollback[] = '$this->imap->deletemb($this->imap->format_user(\''.$mboxname.'\'))';
924 
925  // Decrease the creator's quota...
926  $cur_usr_quota = $this->imap->getquota($this->imap->format_user($this->current_user->mbox));
927  if($this->authenticated_user->a_super == 0 && $cur_usr_quota->is_set) {
928  $result = $this->imap->setquota($this->imap->format_user($this->current_user->mbox), $cur_usr_quota->max - $props['quota']*1024);
929  if(!$result) {
930  $this->ErrorHandler->add_error($this->imap->error_msg);
931  // Rollback
932  $this->rollback($rollback);
933  return false;
934  }
935  $rollback[] = '$this->imap->setquota($this->imap->format_user($this->current_user->mbox), '.$cur_usr_quota->max .'))';
936  $this->ErrorHandler->add_info(sprintf(txt('69'), floor(($cur_usr_quota->max - $props['quota']*1024)/1024) ));
937  } else {
938  $this->ErrorHandler->add_info(txt('71'));
939  }
940 
941  // ... and set the new user's quota.
942  if(is_numeric($props['quota'])) {
943  $result = $this->imap->setquota($this->imap->format_user($mboxname), $props['quota']*1024);
944  if(!$result) {
945  $this->ErrorHandler->add_error($this->imap->error_msg);
946  // Rollback
947  $this->rollback($rollback);
948  return false;
949  }
950  }
951  $this->ErrorHandler->add_info(sprintf(txt('72'), $mboxname, $props['person'], $pw));
952  if(isset($_SESSION['paten'][$props['pate']])) {
953  $_SESSION['paten'][$props['pate']][] = $mboxname;
954  }
955 
956  return true;
957  }
958 
959  /*
960  * $props can be $_POST, as every sutable field from $change is used.
961  */
962  public function mailbox_change($mboxnames, $change, $props) {
963  // Ensure sanity of inputs and check requirements.
964  if(!$this->authenticated_user->a_admin_user > 0) {
965  $this->ErrorHandler->add_error(txt('16'));
966  return false;
967  }
968  if(!$this->validator->validate($props, $change)) {
969  return false;
970  }
971  $mboxnames = $this->mailbox_filter_manipulable($this->authenticated_user->mbox, $mboxnames);
972  if(!count($mboxnames) > 0) {
973  return false;
974  }
975 
976  // Create an array holding every property we have to change.
977  $to_change = array();
978  foreach(array('person', 'canonical', 'pate', 'domains', 'a_admin_domains', 'a_admin_user', 'a_super')
979  as $property) {
980  if(in_array($property, $change)) {
981  if(is_numeric($props[$property])) {
982  $to_change[] = $property.' = '.$props[$property];
983  } else {
984  $to_change[] = $property.'='.$this->db->qstr($props[$property]);
985  }
986  }
987  }
988 
989  // Execute the change operation regarding properties in DB.
990  if(count($to_change) > 0) {
991  $this->db->Execute('UPDATE '.$this->tablenames['user']
992  .' SET '.implode(',', $to_change)
993  .' WHERE '.db_find_in_set($this->db, 'mbox', $mboxnames));
994  }
995 
996  // Manipulate contingents (except quota).
997  foreach(array('max_alias', 'max_regexp') as $what) {
998  if(in_array($what, $change)) {
999  $seek_table = $what == 'max_alias'
1000  ? $this->tablenames['virtual']
1001  : $this->tablenames['virtual_regexp'];
1002  $to_be_processed = $mboxnames;
1003  // Select users which use more aliases than allowed in future.
1004  $result = $this->db->Execute('SELECT COUNT(*) AS consum, owner, person'
1005  .' FROM '.$seek_table.','.$this->tablenames['user']
1006  .' WHERE '.db_find_in_set($this->db, 'owner', $mboxnames).' AND owner=mbox'
1007  .' GROUP BY owner'
1008  .' HAVING consum > '.$props[$what]);
1009  if(!$result === false) {
1010  // We have to skip them.
1011  $have_skipped = array();
1012  while(!$result->EOF) {
1013  $row = $result->fields;
1014  $have_skipped[] = $row['owner'];
1015  if($this->cfg['mboxview_pers']) {
1016  $tmp[] = '<a href="'.mkSelfRef(array('cuser' => $row['owner'])).'" title="'.$row['owner'].'">'.$row['person'].' ('.$row['consum'].')</a>';
1017  } else {
1018  $tmp[] = '<a href="'.mkSelfRef(array('cuser' => $row['owner'])).'" title="'.$row['person'].'">'.$row['owner'].' ('.$row['consum'].')</a>';
1019  }
1020  $result->MoveNext();
1021  }
1022  if(count($have_skipped) > 0) {
1023  $this->ErrorHandler->add_error(sprintf(txt('131'),
1024  $props[$what], $what == 'max_alias' ? txt('88') : txt('89'),
1025  implode(', ', $tmp)));
1026  $to_be_processed = array_diff($to_be_processed, $have_skipped);
1027  }
1028  }
1029  if(count($to_be_processed) > 0) {
1030  // We don't need further checks if a superuser is logged in.
1031  if($this->authenticated_user->a_super > 0) {
1032  $this->db->Execute('UPDATE '.$this->tablenames['user']
1033  .' SET '.$what.'='.$props[$what]
1034  .' WHERE '.db_find_in_set($this->db, 'mbox', $to_be_processed));
1035  } else {
1036  // Now, calculate whether the current user has enough free contingents.
1037  $has_to_be_free = $this->db->GetOne('SELECT SUM('.$props[$what].'-'.$what.')'
1038  .' FROM '.$this->tablenames['user']
1039  .' WHERE '.db_find_in_set($this->db, 'mbox', $to_be_processed));
1040  if($has_to_be_free <= $this->user_get_used_alias($this->current_user->mbox)) {
1041  // If so, set new contingents on the users...
1042  $this->db->Execute('UPDATE '.$this->tablenames['user']
1043  .' SET '.$what.'='.$props[$what]
1044  .' WHERE '.db_find_in_set($this->db, 'mbox', $to_be_processed));
1045  // ... and add/remove the difference from the current user.
1046  $this->db->Execute('UPDATE '.$this->tablenames['user']
1047  .' SET '.$what.'='.$what.'-'.$has_to_be_free
1048  .' WHERE mbox='.$this->db->qstr($this->current_user->mbox));
1049  } else {
1050  // Else, we have to show an error message.
1051  $this->ErrorHandler->add_error(txt('66'));
1052  }
1053  }
1054  }
1055  }
1056  }
1057 
1058  // Change Quota.
1059  if(in_array('quota', $change)) {
1060  $add_quota = 0;
1061  if($this->authenticated_user->a_super == 0) {
1062  foreach($mboxnames as $user) {
1063  if($user != '') {
1064  $quota = $this->imap->get_users_quota($user);
1065  if($quota->is_set)
1066  $add_quota += intval($props['quota'])*1024 - $quota->max;
1067  }
1068  }
1069  $quota = $this->imap->get_users_quota($this->current_user->mbox);
1070  if($add_quota != 0 && $quota->is_set) {
1071  $this->imap->setquota($this->imap->format_user($this->current_user->mbox), $quota->max - $add_quota);
1072  $this->ErrorHandler->add_info(sprintf(txt('78'), floor(($quota->max - $add_quota)/1024) ));
1073  }
1074  }
1075  reset($mboxnames);
1076  foreach($mboxnames as $user) {
1077  if($user != '') {
1078  $result = $this->imap->setquota($this->imap->format_user($user), intval($props['quota'])*1024);
1079  if(!$result) {
1080  $this->ErrorHandler->add_error($this->imap->error_msg);
1081  }
1082  }
1083  }
1084  }
1085 
1086  // Renaming of (single) user.
1087  if(in_array('mbox', $change)) {
1088  if($this->imap->renamemb($this->imap->format_user($mboxnames['0']), $this->imap->format_user($props['mbox']))) {
1089  $this->db->Execute('UPDATE '.$this->tablenames['user'].' SET mbox='.$this->db->qstr($props['mbox']).' WHERE mbox='.$this->db->qstr($mboxnames['0']));
1090  $this->db->Execute('UPDATE '.$this->tablenames['domains'].' SET owner='.$this->db->qstr($props['mbox']).' WHERE owner='.$this->db->qstr($mboxnames['0']));
1091  $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'].'%'));
1092  $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']));
1093  $this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET owner='.$this->db->qstr($props['mbox']).' WHERE owner='.$this->db->qstr($mboxnames['0']));
1094  $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']));
1095  $this->db->Execute('UPDATE '.$this->tablenames['virtual_regexp'].' SET owner='.$this->db->qstr($props['mbox']).' WHERE owner='.$this->db->qstr($mboxnames['0']));
1096  } else {
1097  $this->ErrorHandler->add_error($this->imap->error_msg.'<br />'.txt('94'));
1098  }
1099  }
1100 
1101  if(isset($_SESSION['paten']) && in_array(array('mbox', 'pate'), $change)) {
1102  unset($_SESSION['paten']); // again: inefficient, but maybe we come up with something more elegant
1103  }
1104 
1105  return true;
1106  }
1107 
1108  /*
1109  * If ressources are freed, the current user will get them.
1110  */
1111  public function mailbox_delete($mboxnames) {
1112  $mboxnames = $this->mailbox_filter_manipulable($this->authenticated_user->mbox, $mboxnames);
1113  if(count($mboxnames) == 0) {
1114  return false;
1115  }
1116 
1117  // Delete the given mailboxes from server.
1118  $add_quota = 0; // how many space was actually freed?
1119  $toadd = 0;
1120  $processed = array(); // which users did we delete successfully?
1121  foreach($mboxnames as $user) {
1122  if($user != '') {
1123  // We have to sum up all the space which gets freed in case we later want increase the deleter's quota.
1124  $quota = $this->imap->get_users_quota($user);
1125  if($this->authenticated_user->a_super == 0
1126  && $quota->is_set) {
1127  $toadd = $quota->max;
1128  }
1129  $result = $this->imap->deletemb($this->imap->format_user($user));
1130  if(!$result) { // failure
1131  $this->ErrorHandler->add_error($this->imap->error_msg);
1132  } else { // success
1133  $add_quota += $toadd;
1134  $processed[] = $user;
1135  }
1136  }
1137  }
1138 
1139  // We need not proceed in case no users were deleted.
1140  if(count($processed) == 0) {
1141  return false;
1142  }
1143 
1144  // Now we have to increase the current user's quota.
1145  $quota = $this->imap->get_users_quota($this->current_user->mbox);
1146  if($this->authenticated_user->a_super == 0
1147  && $add_quota > 0
1148  && $quota->is_set) {
1149  $this->imap->setquota($this->imap->format_user($this->current_user->mbox), $quota->max + $add_quota);
1150  $this->ErrorHandler->add_info(sprintf(txt('76'), floor(($quota->max + $add_quota)/1024) ));
1151  }
1152 
1153  // Calculate how many contingents get freed if we delete the users.
1154  if($this->authenticated_user->a_super == 0) {
1155  $result = $this->db->GetRow('SELECT SUM(max_alias) AS nr_alias, SUM(max_regexp) AS nr_regexp'
1156  .' FROM '.$this->tablenames['user']
1157  .' WHERE '.db_find_in_set($this->db, 'mbox', $processed));
1158  if(!$result === false) {
1159  $will_be_free = $result;
1160  }
1161  }
1162 
1163  // virtual
1164  $this->db->Execute('DELETE FROM '.$this->tablenames['virtual'].' WHERE '.db_find_in_set($this->db, 'owner', $processed));
1165  $this->db->Execute('UPDATE '.$this->tablenames['virtual'].' SET active=0, neu=1 WHERE '.db_find_in_set($this->db, 'dest', $processed));
1166  // virtual.regexp
1167  $this->db->Execute('DELETE FROM '.$this->tablenames['virtual_regexp'].' WHERE '.db_find_in_set($this->db, 'owner', $processed));
1168  $this->db->Execute('UPDATE '.$this->tablenames['virtual_regexp'].' SET active=0, neu=1 WHERE '.db_find_in_set($this->db, 'dest', $processed));
1169  // domain (if the one to be deleted owns domains, the deletor will inherit them)
1170  $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));
1171  // user
1172  $this->db->Execute('DELETE FROM '.$this->tablenames['user'].' WHERE '.db_find_in_set($this->db, 'mbox', $processed));
1173  if($this->authenticated_user->a_super == 0 && isset($will_be_free['nr_alias'])) {
1174  $this->db->Execute('UPDATE '.$this->tablenames['user']
1175  .' 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'])
1176  .' WHERE mbox='.$this->db->qstr($this->current_user->mbox));
1177  }
1178  // patenkinder (will be inherited by the one deleting)
1179  $this->db->Execute('UPDATE '.$this->tablenames['user']
1180  .' SET pate='.$this->db->qstr($this->current_user->mbox)
1181  .' WHERE '.db_find_in_set($this->db, 'pate', $processed));
1182 
1183  $this->ErrorHandler->add_info(sprintf(txt('75'), implode(',', $processed)));
1184  if(isset($_SESSION['paten'])) unset($_SESSION['paten']); // inefficient, but maybe we come up with something more elegant
1185 
1186  return true;
1187  }
1188 
1189  /*
1190  * active <-> inactive
1191  */
1192  public function mailbox_toggle_active($mboxnames) {
1193  $tobechanged = $this->mailbox_filter_manipulable($this->current_user->mbox, $mboxnames);
1194  if(count($tobechanged) > 0) {
1195  $this->db->Execute('UPDATE '.$this->tablenames['user']
1196  .' SET active = NOT active'
1197  .' WHERE '.db_find_in_set($this->db, 'mbox', $tobechanged));
1198  if($this->db->Affected_Rows() < 1) {
1199  if($this->db->ErrorNo() != 0) {
1200  $this->ErrorHandler->add_error($this->db->ErrorMsg());
1201  }
1202  } else {
1203  return true;
1204  }
1205  }
1206  return false;
1207  }
1208 
1209 }
1210 ?>
openmailadmin\address_create
address_create($alias, $domain, $arr_destinations)
Definition: openmailadmin.php:266
openmailadmin\get_regexp
get_regexp($match_against=null)
Definition: openmailadmin.php:634
openmailadmin
Definition: openmailadmin.php:6
openmailadmin\user_get_number_domains
user_get_number_domains($username)
Definition: openmailadmin.php:203
current_user
$oma current_user
Definition: _script.php:44
openmailadmin\regexp_toggle_active
regexp_toggle_active($arr_regexp_ids)
Definition: openmailadmin.php:738
$alias
if(isset($_POST['frm']) && $_POST['frm']=='virtual ') $alias
Definition: addresses.php:56
db_find_in_set
db_find_in_set(ADOConnection $db, $needle, array $haystack)
Definition: functions.inc.php:326
openmailadmin\regexp_create
regexp_create($regexp, $arr_destinations)
Definition: openmailadmin.php:673
openmailadmin\address_change_destination
address_change_destination($arr_addresses, $arr_destinations)
Definition: openmailadmin.php:338
openmailadmin\address_toggle_active
address_toggle_active($arr_addresses)
Definition: openmailadmin.php:366
txt
txt($id)
Definition: translation.inc.php:5
openmailadmin\$validator
$validator
Definition: openmailadmin.php:12
$quota
$quota
Definition: index.php:16
$mailboxes
if(isset($_POST['frm']) && $_POST['frm']=='user' && $oma->authenticated_user->a_admin_user >=1) $mailboxes
Definition: mailboxes.php:39
openmailadmin\mailbox_change
mailbox_change($mboxnames, $change, $props)
Definition: openmailadmin.php:962
openmailadmin\get_mailboxes
get_mailboxes()
Definition: openmailadmin.php:759
openmailadmin\domain_check
domain_check(User $reference, $tobechecked, $domain_key)
Definition: openmailadmin.php:427
openmailadmin\get_valid_destinations
get_valid_destinations($possible)
Definition: openmailadmin.php:73
openmailadmin\$current_user
$current_user
Definition: openmailadmin.php:8
openmailadmin\get_domains
get_domains()
Definition: openmailadmin.php:386
openmailadmin\domain_change
domain_change($domains, $change, $data)
Definition: openmailadmin.php:530
find_in_set
find_in_set($str, $strlist)
Definition: functions.inc.php:18
openmailadmin\address_delete
address_delete($arr_addresses)
Definition: openmailadmin.php:315
openmailadmin\$db
$db
Definition: openmailadmin.php:11
openmailadmin\domain_add
domain_add($domain, $props)
Definition: openmailadmin.php:452
openmailadmin\get_user_row
get_user_row($mailbox)
Definition: openmailadmin.php:65
openmailadmin\domain_remove
domain_remove($domains)
Definition: openmailadmin.php:479
openmailadmin\get_mailbox_names
get_mailbox_names()
Definition: openmailadmin.php:49
openmailadmin\user_invalidate_domain_sets
user_invalidate_domain_sets()
Definition: openmailadmin.php:213
openmailadmin\user_is_descendant
user_is_descendant($child, $parent, $levels=7, $cache=array())
Definition: openmailadmin.php:134
openmailadmin\$cfg
$cfg
Definition: openmailadmin.php:16
openmailadmin\get_domain_set
get_domain_set($user, $categories, $cache=true)
Definition: openmailadmin.php:104
$domains
if($oma->authenticated_user->a_admin_domains > 0) $domains
Definition: domains.php:36
openmailadmin\mailbox_toggle_active
mailbox_toggle_active($mboxnames)
Definition: openmailadmin.php:1192
ErrorHandler\add_info
add_info($text)
Definition: ErrorHandler.php:65
openmailadmin\$tablenames
$tablenames
Definition: openmailadmin.php:15
ErrorHandler\getInstance
static getInstance()
Definition: ErrorHandler.php:18
openmailadmin\rollback
rollback($what)
Definition: openmailadmin.php:36
openmailadmin\$editable_domains
$editable_domains
Definition: openmailadmin.php:382
IMAP_Administrator
Definition: IMAP_Administrator.php:2
Password\is_secure
static is_secure($plaintext)
Definition: Password.php:28
openmailadmin\user_change_password
user_change_password($new, $new_repeat, $old_passwd=null)
Definition: openmailadmin.php:606
openmailadmin\mailbox_delete
mailbox_delete($mboxnames)
Definition: openmailadmin.php:1111
openmailadmin\mailbox_filter_manipulable
mailbox_filter_manipulable($who, $desired_mboxes)
Definition: openmailadmin.php:823
openmailadmin\$authenticated_user
$authenticated_user
Definition: openmailadmin.php:9
openmailadmin\regexp_change_destination
regexp_change_destination($arr_regexp_ids, $arr_destinations)
Definition: openmailadmin.php:721
openmailadmin\regex_valid_email
const regex_valid_email
Definition: openmailadmin.php:21
openmailadmin\mailbox_create
mailbox_create($mboxname, $props)
Definition: openmailadmin.php:844
openmailadmin\regexp_delete
regexp_delete($arr_regexp_ids)
Definition: openmailadmin.php:702
openmailadmin\$ErrorHandler
$ErrorHandler
Definition: openmailadmin.php:13
authenticated_user
$oma authenticated_user
Definition: _prepend.php:55
openmailadmin\__construct
__construct(ADOConnection $adodb_handler, array $tablenames, array $cfg, IMAP_Administrator $imap)
Definition: openmailadmin.php:24
openmailadmin\get_selectable_paten
get_selectable_paten($whose)
Definition: openmailadmin.php:795
openmailadmin\regex_valid_alias
const regex_valid_alias
Definition: openmailadmin.php:20
openmailadmin\regex_valid_domain
const regex_valid_domain
Definition: openmailadmin.php:22
ErrorHandler\add_error
add_error($text)
Definition: ErrorHandler.php:72
openmailadmin\get_addresses
get_addresses()
Definition: openmailadmin.php:223
openmailadmin\$imap
$imap
Definition: openmailadmin.php:17
openmailadmin\user_get_number_mailboxes
user_get_number_mailboxes($username)
Definition: openmailadmin.php:193
User
Definition: User.php:7
openmailadmin\user_get_used_alias
user_get_used_alias($username)
Definition: openmailadmin.php:171
InputValidatorSuite
Definition: InputValidatorSuite.php:5
ErrorHandler
Definition: ErrorHandler.php:6