"Fossies" - the Fresh Open Source Software Archive 
Member "openmailadmin-1.0.1/inc/lib/openmailadmin.php" (20 May 2008, 48017 Bytes) of package /linux/privat/old/openmailadmin-1.0.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "openmailadmin.php" see the
Fossies "Dox" file reference documentation.
1 <?php
2 /**
3 * This class collects all methods of Openmailadmin, except for the view
4 * and data storage.
5 */
6 class openmailadmin
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);
30 $this->ErrorHandler = ErrorHandler::getInstance();
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.
75 $pattern = openmailadmin::regex_valid_email;
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;
406 ++$this->editable_domains;
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 }
420 /**
421 * May the new user only select from domains which have been assigned to
422 * the reference user? If so, return true.
423 *
424 * @param reference Instance of User
425 * @param tobechecked Mailbox-name.
426 */
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 {
469 $this->user_invalidate_domain_sets();
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.
512 $this->user_invalidate_domain_sets();
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 }
565 $this->user_invalidate_domain_sets();
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 ******************************** */
601 /**
602 * Changes the current user's password.
603 * This requires the former password for authentication if current user and
604 * authenticated user are the same.
605 */
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 ?>