Improve deletion of aliases and propagated message master
authorJoey Schulze <joey@infodrom.org>
Sat, 7 Oct 2023 22:30:27 +0000 (00:30 +0200)
committerJoey Schulze <joey@infodrom.org>
Sat, 7 Oct 2023 22:30:27 +0000 (00:30 +0200)
15 files changed:
alias.php [new file with mode: 0644]
class/vmail_alias.class.php [new file with mode: 0644]
class/vmail_domain.class.php [new file with mode: 0644]
class/vmail_folder.class.php [new file with mode: 0644]
class/vmail_user.class.php [new file with mode: 0644]
delete.png [new file with mode: 0644]
domain.php
folder.php [new file with mode: 0644]
import.png [new file with mode: 0644]
list.php [new symlink]
templates/delete.phtml [new file with mode: 0644]
templates/folder.phtml [new file with mode: 0644]
user.php
vmail.js [new file with mode: 0644]
vmail.sql

diff --git a/alias.php b/alias.php
new file mode 100644 (file)
index 0000000..515ba1f
--- /dev/null
+++ b/alias.php
@@ -0,0 +1,99 @@
+<?php
+
+if ($_SESSION['mask'] == 'alias') {
+    $title = "Weiterleitungen";
+    $condition = 'islist = 0';
+    $dest_title = "Ziel-Adresse";
+    $dest_type = 'text';
+    $active_title = "Weiterleitung aktiv";
+    $islist = '0';
+} elseif ($_SESSION['mask'] == 'list') {
+    $title = "Mailing-Listen";
+    $condition = 'islist = 1';
+    $dest_title = "Listen-Mitglieder";
+    $dest_type = 'textarea';
+    $active_title = "Liste aktiv";
+    $islist = '1';
+} else {
+    throw new Exception('Unknown mask ' . $_SESSION['mask']);
+}
+
+JavaScript::instance()->file('vmail.js');
+JavaScript::instance()->add("Hallinta.preDelete = alias_pre_delete;");
+
+$mask = array(
+             'table' => 'vmail_alias',
+             'title' => $title,
+             'join' => array('vmail_domain ON vmail_domain_id = vmail_domain.id'),
+             'where' => $condition,
+             'list' => array(
+                             'id' => array(
+                                           'name' => 'ID',
+                                           'visible' => false,
+                                           'sql' => 'vmail_alias.id',
+                                           ),
+                             'name' => array(
+                                           'name' => 'Domain',
+                                           'visible' => false,
+                                           'width' => 200,
+                                           'type' => 'text',
+                                           'filter' => 's',
+                                           ),
+                             'username' => array(
+                                           'name' => 'Username',
+                                           'visible' => false,
+                                           'width' => 200,
+                                           'type' => 'text',
+                                           'filter' => 't',
+                                           ),
+                             'email' => array(
+                                           'name' => 'Adresse',
+                                           'width' => 300,
+                                           'type' => 'text',
+                                           'filter' => 't',
+                                           'sql' => "username || '@' || name",
+                                           ),
+                             'dest' => array(
+                                           'name' => $dest_title,
+                                           'width' => 400,
+                                           'type' => 'text',
+                                           'filter' => 't',
+                                           'sql' => "substring(destination from 1 for 100)",
+                                           ),
+                             'active' => array(
+                                           'name' => 'aktiv',
+                                           'width' => 40,
+                                           'specs' => array('ClassName' => 'aligncenter has-checkbox'),
+                                           'control' => "new Rico.TableColumn.checkbox(1,0,0,1)",
+                                           ),
+                             ),
+             'edit' => array(
+                             'username' => array(
+                                           'name' => 'Local Part',
+                                           'type' => 'text',
+                                           'size' => 21,
+                                           'required' => true,
+                                           ),
+                             'vmail_domain_id' => array(
+                                           'name' => 'Domain',
+                                           'type' => 'select',
+                                           'options' => 'SELECT id,name AS text FROM vmail_domain ORDER BY name',
+                                           'option_empty' => '-- select --',
+                                           'required' => true,
+                                           ),
+                             'destination' => array(
+                                           'name' => $dest_title,
+                                           'type' => $dest_type,
+                                           'rows' => 10,
+                                           'required' => true,
+                                           ),
+                             'islist' => array(
+                                           'type' => 'hidden',
+                                           'default' => $islist,
+                                           ),
+                             'active' => array(
+                                           'name' => $active_title,
+                                           'type' => 'boolean',
+                                           ),
+                             ),
+             );
diff --git a/class/vmail_alias.class.php b/class/vmail_alias.class.php
new file mode 100644 (file)
index 0000000..38abdf2
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+
+class VMail_Alias extends DatabaseTable {
+    public function __construct($id=false)
+    {
+       parent::__construct('vmail_alias', $id);
+    }
+
+    public function deleteFromDestination($email, $commit=false)
+    {
+       $report = '';
+       $email = strtolower($email);
+
+       $sql = sprintf("SELECT id, username, vmail_domain_id, destination FROM vmail_alias WHERE destination ILIKE %s",
+                      $this->db->quote('%'.$email.'%'));
+
+       foreach ($this->db->fetchObjectList($sql) as $row) {
+           $domain = new VMail_Domain($row->vmail_domain_id);
+           $report .= sprintf("Lösche %s aus Alias %s@%s\n",
+                              $email,
+                              $row->username, $domain->get('name'));
+
+           $destination = array_map("trim", preg_split("/(\r?\n|\r|,)/", strtolower(trim($row->destination, " \n\r\t\v\x00,"))));
+
+           if (count($destination) == 1) {
+               $alias = new VMail_Alias($row->id);
+               $report .= $alias->deleteAlias($commit);
+           } else {
+               $idx = array_search($email, $destination);
+               if ($idx !== false) {
+                   unset($destination[$idx]);
+                   if (count($destination) < 5)
+                       $dst_new = implode(", ", $destination);
+                   else
+                       $dst_new = implode("\r\n", $destination);
+                   $sql = sprintf("UPDATE vmail_alias SET destination = %s WHERE id = %d",
+                                  $this->db->quote($dst_new), $row->id);
+                   if ($commit)
+                       $this->db->execute($sql);
+               }
+           }
+       }
+
+       return $report;
+    }
+
+    public function deleteFromRoundcubeIdentities($email, $commit=false)
+    {
+       $report = '';
+
+       foreach (['ROUNDCUBE_DBDRIVER', 'ROUNDCUBE_DBHOST', 'ROUNDCUBE_DBNAME', 'ROUNDCUBE_DBUSER', 'ROUNDCUBE_DBPASS'] AS $key)
+           if (!defined($key))
+               throw new Exception("Configuration define {$key} not set");
+
+       $rdb = new Database(ROUNDCUBE_DBDRIVER, ROUNDCUBE_DBHOST, ROUNDCUBE_DBNAME, ROUNDCUBE_DBUSER, ROUNDCUBE_DBPASS);
+       if (defined('MAIL_ERROR')) $rdb->setErrorMail(MAIL_ERROR);
+
+       $sql = sprintf("SELECT identity_id,name,email,username FROM identities JOIN users using(user_id) WHERE email ILIKE %s",
+                      $rdb->quote($email));
+       foreach ($rdb->fetchObjectList($sql) as $row) {
+           $name = $row->username;
+           if ($row->name)
+               $name .= " (" . $row->name . ")";
+           $report .= sprintf("Lösche %s aus den Roundcube Identitäten des Users %s\n",
+                              $email, $name);
+           $sql = sprintf("DELETE FROM identities WHERE identity_id = %d", $row->identity_id);
+           if ($commit)
+               $rdb->execute($sql);
+       }
+
+       $parts = explode('@', $email);
+       $sql = sprintf("SELECT user_id FROM users WHERE username = %s", $rdb->quote($parts[0]));
+       foreach ($rdb->fetchObjectList($sql) as $row) {
+           $report .= sprintf("Lösche User %s aus Roundcube Usern\n", $parts[0]);
+           $sql = sprintf("DELETE FROM users WHERE user_id = %d", $row->user_id);
+           if ($commit)
+               $rdb->execute($sql);
+       }
+
+       return $report;
+    }
+
+    public function deleteAlias($commit=false)
+    {
+       if (!$this->id)
+           throw new Exception("No user id provided");
+
+       $report = '';
+
+       $domain = new VMail_Domain($this->data->vmail_domain_id);
+       $email = sprintf("%s@%s", $this->data->username, $domain->get('name'));
+
+       $report .= $this->deleteFromDestination($email, $commit);
+
+       if (defined('ROUNDCUBE_DBDRIVER')) {
+           $report .= $this->deleteFromRoundcubeIdentities($email, $commit);
+       }
+
+       $report .= sprintf("Lösche %s aus Aliasen\n", $email);
+       $sql = sprintf("DELETE FROM %s WHERE id = %d", $this->table, $this->id);
+       if ($commit)
+           $this->db->execute($sql);
+
+       return $report;
+    }
+
+    public function ajaxDeleteReport(Array $data)
+    {
+       $report = $this->deleteAlias(false);
+       return ['report' => nl2br($report)];
+    }
+
+    public function ajaxDeleteAlias(Array $data)
+    {
+       $domain = new VMail_Domain($this->data->vmail_domain_id);
+       $report = $this->deleteAlias(true);
+
+       return ['text' => sprintf("Alias %s@%s gelöscht", $this->data->username, $domain->get('name'))];
+    }
+}
diff --git a/class/vmail_domain.class.php b/class/vmail_domain.class.php
new file mode 100644 (file)
index 0000000..c30fc01
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+
+class VMail_Domain extends DatabaseTable {
+    public function __construct($id=false)
+    {
+       parent::__construct('vmail_domain', $id);
+    }
+}
diff --git a/class/vmail_folder.class.php b/class/vmail_folder.class.php
new file mode 100644 (file)
index 0000000..d0c1b36
--- /dev/null
@@ -0,0 +1,151 @@
+<?php
+
+class VMail_Folder extends DatabaseTable {
+    public function __construct($id=false)
+    {
+       parent::__construct('vmail_folder', $id);
+    }
+
+    protected function setMailboxPermissions($row, $value, $other_id)
+    {
+       $sql = sprintf("SELECT username || '@' || name
+FROM vmail_user
+JOIN vmail_domain ON vmail_domain.id = vmail_domain_id
+WHERE vmail_user.id = %d",
+                      $other_id);
+       $other = $this->db->fetchValue($sql);
+
+       if ($value) {
+           $sql = sprintf("SELECT id FROM vmail_folder_user WHERE vmail_folder_id = %d AND vmail_user_id = %d",
+                          $row->vmail_folder_id,
+                          $other_id);
+           $id = $this->db->fetchValue($sql);
+           if ($id)
+               return true;
+
+           $sql = sprintf("INSERT INTO vmail_folder_user (vmail_folder_id,vmail_user_id,sys_user,sys_edit) VALUES (%d,%d,%s,now())",
+                          $row->vmail_folder_id,
+                          $other_id,
+                          $this->db->quote($_SESSION['sys']['login']));
+
+           $cmd = sprintf('sudo -u vmail /etc/dovecot/share-folder-add %s %s %s > /dev/null 2>&1',
+                          $row->from_user, $row->folder, $other);
+       } else {
+           $sql = sprintf("DELETE FROM vmail_folder_user WHERE vmail_folder_id = %d AND vmail_user_id = %d",
+                          $row->vmail_folder_id,
+                          $other_id);
+
+           $cmd = sprintf('sudo -u vmail /etc/dovecot/share-folder-del %s %s %s > /dev/null 2>&1',
+                          $row->from_user, $row->folder, $other);
+       }
+
+       debug($cmd);
+       if (file_exists('/etc/dovecot/share-folder-add'))
+           system($cmd);
+
+       return $this->db->execute($sql);
+    }
+
+    public function ajaxAdd(Array $data)
+    {
+       $sql = sprintf("INSERT INTO %s (vmail_user_id,folder,sys_user,sys_edit) VALUES (%d,%s,%s,now())",
+                      $this->table,
+                      $data['vmail_user_id'],
+                      $this->db->quote($data['folder']),
+                      $this->db->quote($_SESSION['sys']['login']));
+
+       return $this->db->execute($sql);
+    }
+
+    public function ajaxDelete(Array $data)
+    {
+       if (!$this->id)
+           throw new Exception("No folder id provided");
+
+       $sql = sprintf("SELECT username || '@' || name AS from_user, folder, vmail_folder.id AS vmail_folder_id
+FROM vmail_folder
+JOIN vmail_user ON vmail_user.id = vmail_user_id
+JOIN vmail_domain ON vmail_domain.id = vmail_domain_id
+WHERE vmail_folder.id = %d",
+                      $this->id);
+       $row = $this->db->fetchObject($sql);
+
+       $sql = "SELECT id FROM vmail_user";
+       foreach ($this->db->fetchObjectList($sql) as $urow)
+           $this->setMailboxPermissions($row, 0, $urow->id);
+
+       $sql = sprintf("DELETE FROM vmail_folder WHERE id = %d", $this->id);
+
+       return $this->db->execute($sql);
+    }
+
+    public function ajaxNew(Array $data)
+    {
+       $sql = sprintf("SELECT username || '@' || name
+FROM vmail_user
+JOIN vmail_domain ON vmail_domain.id = vmail_domain_id
+WHERE vmail_user.id = %d",
+                      $data['vmail_user_id']);
+       $user = $this->db->fetchValue($sql);
+
+       $cmd = sprintf('sudo -u vmail /etc/dovecot/folder-add %s %s > /dev/null 2>&1',
+                      $user, escapeshellarg($data['name']));
+
+       debug($cmd);
+       if (file_exists('/etc/dovecot/folder-add'))
+           system($cmd);
+    }
+
+    public function ajaxCheckbox(Array $data)
+    {
+       if (!$this->id)
+           throw new Exception("No folder id provided");
+
+       $sql = sprintf("SELECT id FROM vmail_folder_user WHERE vmail_folder_id = %d AND vmail_user_id = %d",
+                      $this->id, $data['vmail_user_id']);
+       $connection = $this->db->fetchValue($sql);
+
+       if ($data['value']) {
+           if (!$connection) {
+               $sql = sprintf("INSERT INTO vmail_folder_user (vmail_folder_id, vmail_user_id, sys_user, sys_edit) VALUES (%d, %d, %s, now())",
+                              $this->id, $data['vmail_user_id'], $this->db->quote($this->username));
+               $this->db->execute($sql);
+           }
+       } else {
+           if ($connection) {
+               $sql = sprintf("DELETE FROM vmail_folder_user WHERE id = %d", $connection);
+               $this->db->execute($sql);
+           }
+       }
+
+       $sql = sprintf("SELECT username || '@' || name AS from_user, folder, vmail_folder.id AS vmail_folder_id
+FROM vmail_folder
+JOIN vmail_user ON vmail_user.id = vmail_user_id
+JOIN vmail_domain ON vmail_domain.id = vmail_domain_id
+WHERE vmail_folder.id = %d",
+                      $this->id);
+       $row = $this->db->fetchObject($sql);
+
+       return $this->setMailboxPermissions($row, $data['value'], $this->id);
+    }
+
+    public function ajaxCheckboxAll(Array $data)
+    {
+       if (!$this->id)
+           throw new Exception("No folder id provided");
+
+       $sql = sprintf("SELECT username || '@' || name AS from_user, folder, vmail_folder.id AS vmail_folder_id
+FROM vmail_folder
+JOIN vmail_user ON vmail_user.id = vmail_user_id
+JOIN vmail_domain ON vmail_domain.id = vmail_domain_id
+WHERE vmail_folder.id = %d",
+                      $this->id);
+       $row = $this->db->fetchObject($sql);
+
+       $sql = "SELECT id FROM vmail_user WHERE active = 1";
+       foreach ($this->db->fetchObjectList($sql) as $urow)
+           $this->setMailboxPermissions($row, $data['value'], $urow->id);
+
+       return true;
+    }
+}
diff --git a/class/vmail_user.class.php b/class/vmail_user.class.php
new file mode 100644 (file)
index 0000000..9a27c6d
--- /dev/null
@@ -0,0 +1,312 @@
+<?php
+
+class VMail_User extends DatabaseTable {
+    public function __construct($id=false)
+    {
+       parent::__construct('vmail_user', $id);
+    }
+
+    public static function getUserList()
+    {
+       if (defined('VMAIL_SHARED_USER')) {
+           $email = Database::get()->quote(VMAIL_SHARED_USER);
+           $sql = <<<EOS
+SELECT vmail_user.id,username || '@' || name AS text
+FROM vmail_user
+JOIN vmail_domain ON vmail_domain.id = vmail_domain_id
+WHERE username || '@' || name = {$email}
+ORDER BY text
+EOS;
+       } else {
+           $sql = <<<EOS
+SELECT vmail_user.id,username || '@' || name AS text
+FROM vmail_user
+JOIN vmail_domain ON vmail_domain.id = vmail_domain_id
+ORDER BY text
+EOS;
+       }
+
+       return Database::get()->fetchObjectList($sql);
+    }
+
+    /*
+     * Import from Password Plugin for Roundcube: password.php
+     * distributed under the GNU GPL v3 or later
+     * Author Aleksander Machniak <alec@alec.pl>
+     */
+    protected static function random_salt($length)
+    {
+       $possible = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./';
+       $str      = '';
+
+       while (strlen($str) < $length) {
+           $str .= substr($possible, (rand() % strlen($possible)), 1);
+       }
+
+       return $str;
+    }
+
+    protected static function encryptPasswd_encrypt($pw)
+    {
+       if (!defined('VMAIL_CRYPT_HASH'))
+           return $pw;
+
+       switch (VMAIL_CRYPT_HASH) {
+       case 'MD5': return md5($pw); break;
+       case 'MD5-CRYPT': return crypt($pw, '$1$' . static::random_salt(9)); break;
+       case 'SHA256-CRYPT': return crypt($pw, '$5$' . static::random_salt(16)); break;
+       case 'SHA512-CRYPT': return crypt($pw, '$6$' . static::random_salt(16)); break;
+       default: throw new Exception('Please configure VMAIL_CRYPT_HASH properly');
+       }
+    }
+
+    protected function deleteFromSharedFolder($email, $commit=false)
+    {
+       $report = '';
+
+       $report .= "Suche Mitgliedschaft in gemeinsamen Mail-Foldern\n";
+       $sql = <<<EOS
+           SELECT vmail_folder_user.id, vmail_folder_id, username || '@' || name AS from_user, folder
+           FROM vmail_folder_user
+           JOIN vmail_folder ON vmail_folder.id = vmail_folder_id
+           JOIN vmail_user ON vmail_user.id = vmail_folder.vmail_user_id
+           JOIN vmail_domain ON vmail_domain.id = vmail_domain_id
+           WHERE vmail_folder_user.vmail_user_id = %d
+EOS;
+
+       $sql = sprintf($sql, $this->id);
+
+       foreach ($this->db->fetchObjectList($sql) as $row) {
+           $report .= sprintf("Lösche %s aus gemeinsamem Mail-Folder %s\n",
+                              $email, $row->folder);
+
+           $cmd = sprintf('sudo -u vmail /etc/dovecot/share-folder-del %s %s %s > /dev/null 2>&1',
+                  $row->from_user, $row->folder, $email);
+           debug($cmd);
+           if ($commit && file_exists('/etc/dovecot/share-folder-add'))
+               system($cmd);
+
+           $sql = sprintf("DELETE FROM vmail_folder_user WHERE id = %d",
+                          $row->id);
+           if ($commit)
+               $this->db->execute($sql);
+       }
+
+       return $report;
+    }
+
+    public function deleteUser($commit=false)
+    {
+       if (!$this->id)
+           throw new Exception("No user id provided");
+
+       $report = '';
+
+       $domain = new VMail_Domain($this->data->vmail_domain_id);
+       $email = sprintf("%s@%s", $this->data->username, $domain->get('name'));
+
+       $alias = new VMail_Alias();
+       $report .= $alias->deleteFromDestination($email, $commit);
+
+       $report .= $this->deleteFromSharedFolder($email, $commit);
+
+       if (defined('ROUNDCUBE_DBDRIVER')) {
+           $report .= $alias->deleteFromRoundcubeIdentities($email, $commit);
+       }
+
+       $report .= sprintf("Lösche %s aus Mailboxen\n", $email);
+       $sql = sprintf("DELETE FROM %s WHERE id = %d", $this->table, $this->id);
+       if ($commit)
+           $this->db->execute($sql);
+
+       return $report;
+    }
+
+    public function ajaxCreateMailbox(Array $data)
+    {
+       if (!$this->id)
+           throw new Exception("No user id provided");
+
+       debug('/etc/dovecot/auto-create-mailbox');
+       if (!file_exists('/etc/dovecot/auto-create-mailbox')) return true;
+
+       $sql = sprintf("SELECT username,name FROM vmail_user JOIN vmail_domain ON vmail_domain.id = vmail_domain_id WHERE vmail_user.id = %d",
+                      $this->id);
+
+       $row = $this->db->fetchObject($sql);
+
+       $f = popen('sudo -u vmail /etc/dovecot/auto-create-mailbox', 'w');
+       if ($f === false) return false;
+
+       fwrite($f, $row->name . "\n");
+       fwrite($f, $row->username . "\n");
+       pclose($f);
+
+       $rdb = new Database(ROUNDCUBE_DBDRIVER, ROUNDCUBE_DBHOST, ROUNDCUBE_DBNAME, ROUNDCUBE_DBUSER, ROUNDCUBE_DBPASS);
+       if (defined('MAIL_ERROR')) $rdb->setErrorMail(MAIL_ERROR);
+
+       $sql = sprintf("INSERT INTO users (username, mail_host, created, language) VALUES (%s, %s, now(), %s)",
+                      $rdb->quote($row->username),
+                      $rdb->quote('localhost'),
+                      $rdb->quote('de_DE'));
+
+       $rdb->execute($sql);
+       $user_id = $rdb->lastInsertId();
+       if ($user_id == 0) {
+           $sql = sprintf("SELECT user_id FROM users WHERE username = %s",
+                          $rdb->quote($row->username));
+           $user_id = $rdb->fetchValue($sql);
+       }
+
+       $sql = sprintf("INSERT INTO identities (user_id, changed, del, standard, name, email) VALUES (%d, now(), 0, 1, %s, %s)",
+                      $user_id,
+                      $rdb->quote($row->username),
+                      $rdb->quote($row->username.'@'.$row->name));
+       $rdb->execute($sql);
+
+       return true;
+    }
+
+    public function ajaxSetPassword(Array $data)
+    {
+       return $this->modify(['password' => static::encryptPasswd_encrypt($data['passwd'])]);
+    }
+
+    public function ajaxSieveCopy(Array $data)
+    {
+       if (!$this->id)
+           throw new Exception("No user id provided");
+
+       debug('/etc/dovecot/sieve-copy');
+       if (!file_exists('/etc/dovecot/sieve-copy'))
+           return true;
+
+       $sql = sprintf("SELECT username,name FROM vmail_user JOIN vmail_domain ON vmail_domain.id = vmail_domain_id WHERE vmail_user.id = %d",
+                      $this->id);
+
+       $row = $this->db->fetchObject($sql);
+
+       $f = popen('sudo -u vmail /etc/dovecot/sieve-copy', 'w');
+       if ($f === false) return false;
+
+       fwrite($f, $row->name . "\n");
+       fwrite($f, $row->username . "\n");
+       pclose($f);
+
+
+       $rdb = new Database(ROUNDCUBE_DBDRIVER, ROUNDCUBE_DBHOST, ROUNDCUBE_DBNAME, ROUNDCUBE_DBUSER, ROUNDCUBE_DBPASS);
+       if (defined('MAIL_ERROR')) $rdb->setErrorMail(MAIL_ERROR);
+
+       $sql = sprintf("SELECT name,email FROM identities JOIN users using(user_id) WHERE username = %s AND email not like '%%@localhost'",
+                      $rdb->quote($row->username));
+       $row = $rdb->fetchObject($sql);
+
+       if ($row === false) {
+           return array('need_identity' => true);
+       } else {
+           $sql = "SELECT user_id FROM users WHERE username = 'shared'";
+           $shared_id = $rdb->fetchValue($sql);
+
+           $sql = sprintf("SELECT identity_id FROM identities WHERE user_id = %d AND email = %s",
+                          $shared_id, $rdb->quote($row->email));
+           $identity = $rdb->fetchValue($sql);
+
+           if ($identity === false) {
+               $sql = sprintf("INSERT INTO identities (user_id, changed, del, standard, name, email) " .
+                              "VALUES (%d, now(), 0, 1, %s, %s)",
+                              $shared_id, $rdb->quote($row->name), $rdb->quote($row->email));
+               $rdb->execute($sql);
+           }
+       }
+
+       return ['need_identity' => false];
+    }
+
+    public function ajaxSieveRestore(Array $data)
+    {
+       if (!$this->id)
+           throw new Exception("No user id provided");
+
+       debug('/etc/dovecot/sieve-retrieve');
+       if (!file_exists('/etc/dovecot/sieve-retrieve'))
+           return true;
+
+       $sql = sprintf("SELECT username,name FROM vmail_user JOIN vmail_domain ON vmail_domain.id = vmail_domain_id WHERE vmail_user.id = %d",
+                      $this->id);
+
+       $row = $this->db->fetchObject($sql);
+
+       $f = popen('sudo -u vmail /etc/dovecot/sieve-retrieve', 'w');
+       if ($f === false) return false;
+
+       fwrite($f, $row->name . "\n");
+       fwrite($f, $row->username . "\n");
+       pclose($f);
+
+
+       $rdb = new Database(ROUNDCUBE_DBDRIVER, ROUNDCUBE_DBHOST, ROUNDCUBE_DBNAME, ROUNDCUBE_DBUSER, ROUNDCUBE_DBPASS);
+       if (defined('MAIL_ERROR')) $rdb->setErrorMail(MAIL_ERROR);
+
+       $sql = sprintf("SELECT name,email FROM identities JOIN users using(user_id) WHERE username = %s AND email not like '%%@localhost'",
+                      $rdb->quote($row->username));
+       $row = $rdb->fetchObject($sql);
+
+       if ($row !== false) {
+           $sql = "SELECT user_id FROM users WHERE username = 'shared'";
+           $shared_id = $rdb->fetchValue($sql);
+
+           $sql = sprintf("DELETE FROM identities WHERE user_id = %d AND email = %s",
+                          $shared_id, $rdb->quote($row->email));
+           $rdb->execute($sql);
+       }
+
+       return true;
+    }
+
+    public function ajaxDeleteReport(Array $data)
+    {
+       $report = $this->deleteUser(false);
+
+       return ['report' => nl2br($report)];
+    }
+
+    public function ajaxDeleteUser(Array $data)
+    {
+       $report = $this->deleteUser(true);
+
+       return ['text' => sprintf("Mailbox %s gelöscht", $this->data->username)];
+    }
+
+    public function ajaxGetFolderList(Array $data)
+    {
+       if (!$this->id)
+           throw new Exception("No user id provided");
+
+       $sql = sprintf("SELECT folder FROM vmail_folder WHERE vmail_user_id = %d", $this->id);
+
+       $current_list = array();
+       foreach ($this->db->fetchObjectList($sql) as $row)
+           $current_list[] = $row->folder;
+
+       $sql = sprintf("SELECT username || '@' || name FROM vmail_user JOIN vmail_domain ON vmail_domain.id = vmail_domain_id WHERE vmail_user.id = %d",
+                      $this->id);
+       $name = $this->db->fetchValue($sql);
+
+       $options = array();
+
+       debug('/etc/dovecot/discover-folderlist ' . $name);
+       if (file_exists('/etc/dovecot/discover-folderlist')) {
+           if (($f = popen(sprintf('sudo -u vmail /etc/dovecot/discover-folderlist %s', $name), 'r')) !== false) {
+               while (!feof($f)) {
+                   $line = trim(fgets($f));
+                   if (!strlen($line)) continue;
+                   if (in_array($line, $current_list)) continue;
+                   $options[] = array('id' => $line, 'text' => $line);
+               }
+               pclose($f);
+           }
+       }
+
+       return ['options' => $options];
+    }
+}
diff --git a/delete.png b/delete.png
new file mode 100644 (file)
index 0000000..e2085af
Binary files /dev/null and b/delete.png differ
index c7ba97d..1368ce6 100644 (file)
@@ -22,4 +22,3 @@ $mask = array(
                                            ),
                              ),
              );
-?>
diff --git a/folder.php b/folder.php
new file mode 100644 (file)
index 0000000..a42d0d5
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+
+JavaScript::instance()->file('lib/ricoTableColumnDB.js');
+JavaScript::instance()->file('vmail.js');
+
+Actions::instance()->addLink(new Link(array('id' => 'btn_import',
+                                           'icon' => Hallinta::instance()->urlbase().'masks/vmail/import.png',
+                                           'title' => 'Neuen Shared Folder hinzufügen',
+                                           'function' => 'discover_folder')));
+
+$mask = array(
+             'title' => 'Shared Folder',
+             'table' => 'vmail_folder',
+             'join' => array('vmail_user ON vmail_user_id = vmail_user.id',
+                             'vmail_domain ON vmail_domain_id = vmail_domain.id'),
+             'list' => array(
+                             'id' => array(
+                                           'name' => 'ID',
+                                           'visible' => false,
+                                           'sql' => 'vmail_folder.id',
+                                           ),
+                             'folder' => array(
+                                           'name' => 'Folder',
+                                           'width' => 200,
+                                           'type' => 'text',
+                                           'filter' => 't',
+                                           ),
+                             'name' => array(
+                                           'name' => 'Domain',
+                                           'visible' => false,
+                                           'width' => 200,
+                                           'type' => 'text',
+                                           'filter' => 's',
+                                           ),
+                             'username' => array(
+                                           'name' => 'User',
+                                           'visible' => false,
+                                           'width' => 200,
+                                           'type' => 'text',
+                                           'filter' => 't',
+                                           ),
+                             'email' => array(
+                                           'name' => 'Username',
+                                           'width' => 300,
+                                           'type' => 'text',
+                                           'filter' => 't',
+                                           'sql' => "username || '@' || name",
+                                           ),
+                             'delete' => array(
+                                           'name' => 'Del',
+                                           'width' => 25,
+                                           'specs' => array('ClassName' => 'aligncenter', 'canSort' => false),
+                                           'control' => "new Rico.TableColumn.link('javascript:folder_del({0})')",
+                                           'sql' => "'<img src=\"".Hallinta::instance()->urlbase()."masks/vmail/delete.png\" title=\"Löschen\">'",
+                                           ),
+                             ),
+             'second' => array(
+                 'perms' => array(
+                        'title' => 'Freigaben',
+                        'table' => 'vmail_user',
+                        'rows' => 10,
+                        'width' => 400,
+                        'join' => array('vmail_domain ON vmail_domain_id = vmail_domain.id'),
+                        'where' => 'vmail_user.id <> (SELECT vmail_user_id FROM vmail_folder WHERE id = {id}) AND active = 1',
+                        'list' => array(
+                                'id' => array(
+                                              'name' => 'ID',
+                                              'visible' => false,
+                                              'sql' => 'vmail_user.id',
+                                              ),
+                                'name' => array(
+                                                'name' => 'Domain',
+                                                'visible' => false,
+                                                'width' => 200,
+                                                'type' => 'text',
+                                                'filter' => 's',
+                                                ),
+                                'username' => array(
+                                                    'name' => 'Username',
+                                                    'visible' => false,
+                                                    'width' => 200,
+                                                    'type' => 'text',
+                                                    'filter' => 't',
+                                                    ),
+                                'email' => array(
+                                                 'name' => 'Username',
+                                                 'width' => 300,
+                                                 'type' => 'text',
+                                                 'sql' => "username || '@' || name",
+                                                 'filter' => 't',
+                                                 ),
+                                'checked' => array(
+                                                   'name' => 'OK',
+                                                   'width' => 40,
+                                                   'sql' => '(SELECT count(*)
+                                                               FROM vmail_folder_user
+                                                               WHERE vmail_user_id = vmail_user.id
+                                                                 AND vmail_folder_id = {id})',
+                                                   'control' => "new Rico.TableColumn.checkboxFunction(folder_checkbox_change, folder_checkbox_all)",
+                                                   'filter' => 'c',
+                                                   'specs' => array('ClassName' => 'aligncenter has-checkbox', 'canSort' => false),
+                                                   ),
+                                        ),
+                                  ),
+                               ),
+             );
diff --git a/import.png b/import.png
new file mode 100644 (file)
index 0000000..710385b
Binary files /dev/null and b/import.png differ
diff --git a/list.php b/list.php
new file mode 120000 (symlink)
index 0000000..fe5736a
--- /dev/null
+++ b/list.php
@@ -0,0 +1 @@
+alias.php
\ No newline at end of file
diff --git a/templates/delete.phtml b/templates/delete.phtml
new file mode 100644 (file)
index 0000000..56bb9f7
--- /dev/null
@@ -0,0 +1,11 @@
+<div>
+<div id="report" style="margin-top:1ex;margin-bottom:3ex;font-weight:normal;width:500px;max-height:500px;overflow-y:auto;">
+</div>
+
+<div style="text-align:center;margin-bottom:1ex;">
+<button id="btn_delete_really" class="builtin" onclick="delete_confirmation(this)" title="Wirklich löschen">Wirklich Löschen</button>
+&nbsp;
+<button id="btn_delete_close" class="builtin" onclick="delete_popup.closePopup()" title="Schließen">Abbrechen</button>
+</div>
+
+</div>
diff --git a/templates/folder.phtml b/templates/folder.phtml
new file mode 100644 (file)
index 0000000..02ce74b
--- /dev/null
@@ -0,0 +1,19 @@
+<div class="form" style="margin-left: 10px; margin-right: 12px; padding-left: 10px; padding-bottom: 7px;">
+<label for="discover_vmail_user_id">Username:</label><br>
+<select name="vmail_user_id" id="discover_vmail_user_id" onchange="discover_folderlist()">
+<option value="">-- bitte wählen --</option>
+<?php foreach (VMail_User::getUserList() as $row) { ?>
+<option value="<?php echo $row->id; ?>"><?php echo $row->text; ?></option>
+<?php } ?>
+</select>
+<div style="height: 5px;"></div>
+<?php if (file_exists('/etc/dovecot/folder-add')) { ?>
+<label for="new_folder">Neuer Folder:</label><br>
+<input name="new_folder" id="new_folder">
+&nbsp;&nbsp;<button onclick="return folder_new()">Anlegen</button>
+<br>
+<?php } ?>
+<label for="discover_folder">Shared Folder:</label><br>
+<select name="folder" id="discover_folder"><option value="">-- bitte wählen --</option></select>
+&nbsp;&nbsp;<button onclick="return folder_add()">Hinzufügen</button>
+</div>
index d3a4426..d0e909e 100644 (file)
--- a/user.php
+++ b/user.php
 <?php
 
-
 $buttons = <<<EOC
 <p style="margin-top: 5px; margin-bottom: 4px; text-align: center;">
-<button class="custom" onclick="return passwd_delete()">Passwort löschen</button>
 <button class="custom" onclick="return passwd_set()">Passwort setzen</button>
 </p>
 EOC;
 
-$jscode[] = <<<EOC
-var post_save = user_post_save;
-var pre_insert = user_pre_save;
-var pre_save = user_pre_save;
-
-function user_pre_save()
-{
-  if ($('edit_username').value == '*' && !$('edit_forward').value.length) {
-    alert("Fehler aufgetreten!\\n\\nFür Catch-All-Einträge muß zwingend\\neine Forward-Adresse angegeben werden.");
-    $('edit_forward').focus();
-    return false;
-  }
-
-  return true;
-}
-
-function user_post_save()
-{
-  grid_update(grid);
-
-  if (!$('edit_id').value.length && !$('edit_forward').value.length)
-    alert("Bitte als nächstes ein Passwort setzen.\\nUnd danach direkt mit Mailprogram aktivieren.");
-}
-
-function passwd_delete_callback(data)
-{
-    grid_update(grid);
-    info('Preis gespeichert');
-}
-
-function passwd_delete()
-{
-    var eid = document.getElementById('edit_id');
-    if (!eid || !eid.value.length) return false;
-
-    var source = document.getElementById('source');
-    if (!source) return false;
-
-    var parms = 'source=' + source.innerHTML + '&callback=delete&';
-    parms += 'id=' + eid.value;
-
-    ajax_request('function', parms, passwd_delete_callback);
-
-    return false;
-}
-
-var passwd_popup = false;
-function passwd_setpw_callback(data)
-{
-    grid_update(grid);
-    info('Neues Passwort gespeichert');
-}
+if (file_exists('/etc/dovecot/sieve-copy') && file_exists('/etc/dovecot/sieve-retrieve') && defined('SIEVEADDR')) {
+    $title_copy = sprintf("Sieve-Skript zu %s kopieren", SIEVEADDR);
+    $title_retrieve = sprintf("Sieve-Skript von %s zurückholen", SIEVEADDR);
 
-function passwd_setpw()
-{
-  passwd_popup.closePopup();
-
-  var pw1 = document.getElementById('pw_pass1');
-  var pw2 = document.getElementById('pw_pass2');
-
-  if (!pw1.value.length || !pw2.value.length || pw1.value != pw2.value) {
-    alert("Die Passwörter stimmen nicht überein!");
-    return false;
-  }
-
-  var source = document.getElementById('source');
-  var pw_id = document.getElementById('pw_id');
-  var pw_pass = document.getElementById('pw_pass1');
-
-  var parms = 'source=' + source.innerHTML + '&callback=setpw';
-  parms += '&id=' + pw_id.value;
-  parms += '&passwd=' + pw_pass.value;
-
-  ajax_request('function', parms, passwd_setpw_callback);
+    $sieve_buttons = <<<EOC
+<div style="border: 1px solid #BBB; padding-left: 2px; padding-right: 2px;">
+<p style="margin-top: 5px; margin-bottom: 4px; text-align: left;">
+Sieve-Skript (u.a. Vacation) bearbeiten
+</p>
+<p style="margin-top: 5px; margin-bottom: 4px; text-align: center;">
+<button class="custom" onclick="return sieve_copy()" title="$title_copy">Skript kopieren</button>
+<button class="custom" onclick="return sieve_retrieve()" title="$title_retrieve">Skript zurückholen</button>
+</p>
+</div>
+EOC;
 
-  return false;
+    $buttons .= $sieve_buttons;
 }
 
-function passwd_set()
-{
-  var edit_id = document.getElementById('edit_id');
-
-  if (!edit_id.value.length) return false;
-
-  var width = 245;
-  var height = 163;
-
-  if (!passwd_popup) {
-    var options = {hideOnClick: false, canDragFunc: true };
-    passwd_popup = new Rico.Popup(options);
-    passwd_popup.createWindow('<b>Neues Passwort setzen</b>','',height+'px',width+'px');
-    passwd_popup.contentDiv.style.backgroundColor='#e0e0e0';
-    passwd_popup.contentDiv.innerHTML = [
-                                        '<div class="form" style="margin-left: 10px; margin-right: 12px; padding-left: 10px; padding-bottom: 7px;">',
-                                        '<input type="hidden" name="pw_id" id="pw_id">',
-                                        '<label for="pw_email">E-Mail Adresse:</label><br>',
-                                        '<input type="text" name="pw_email" id="pw_email" size="23" readonly>',
-                                        '<div style="height: 5px;"></div>',
-                                        '<label for="pw_pass1">Neues Passwort:</label><br>',
-                                        '<input type="password" name="pw_pass1" id="pw_pass1" size="23">',
-                                        '<div style="height: 5px;"></div>',
-                                        '<label for="pw_pass2">erneut eingeben:</label><br>',
-                                        '<input type="password" name="pw_pass2" id="pw_pass2" size="23">',
-                                        '<div style="height: 5px;"></div>',
-                                        '<button onclick="return passwd_setpw()">Passwort setzen</button>',
-                                        '</div>',
-                                        ].join('');
-  }
-
-  var edit_username = document.getElementById('edit_username');
-  var edit_vmail_domain_id = document.getElementById('edit_vmail_domain_id');
-
-  var pw_id = document.getElementById('pw_id');
-  var pw_email = document.getElementById('pw_email');
-
-  pw_id.value = edit_id.value;
-  pw_email.value = edit_username.value + '@' + edit_vmail_domain_id.options[edit_vmail_domain_id.selectedIndex].innerHTML;
-
-  var x = Math.floor((RicoUtil.windowWidth()-width)/2);
-  var y = Math.floor((RicoUtil.windowHeight()-height)/2);
-  passwd_popup.openPopup(x,y);
+JavaScript::instance()->file('vmail.js');
+JavaScript::instance()->add("Hallinta.preSave = user_pre_save;");
+JavaScript::instance()->add("Hallinta.preInsert = user_pre_save;");
+JavaScript::instance()->add("Hallinta.postInsert = user_post_insert;");
+JavaScript::instance()->add("Hallinta.preDelete = user_pre_delete;");
+JavaScript::instance()->add("Hallinta.fetchItemAfterInsert = true;");
 
-  var pw_pass1 = document.getElementById('pw_pass1');
-  pw_pass1.value = '';
-  var pw_pass2 = document.getElementById('pw_pass2');
-  pw_pass2.value = '';
-  pw_pass1.focus();
-
-  return false;
-}
-EOC;
+if (defined('SIEVEADDR'))
+    JavaScript::instance()->add(sprintf("var SIEVEADDR = '%s';", SIEVEADDR));
 
 $mask = array(
              'table' => 'vmail_user',
-             'title' => 'Mailboxen und Weiterleitungen',
+             'title' => 'Mailboxen und Adressen',
              'join' => array('vmail_domain ON vmail_domain_id = vmail_domain.id'),
              'list' => array(
                              'id' => array(
@@ -150,35 +45,37 @@ $mask = array(
                                            'visible' => false,
                                            'sql' => 'vmail_user.id',
                                            ),
+                             'name' => array(
+                                           'name' => 'Domain',
+                                           'visible' => false,
+                                           'width' => 200,
+                                           'type' => 'text',
+                                           'filter' => 's',
+                                           ),
+                             'username' => array(
+                                           'name' => 'Username',
+                                           'visible' => false,
+                                           'width' => 200,
+                                           'type' => 'text',
+                                           'filter' => 't',
+                                           ),
                              'email' => array(
                                            'name' => 'E-Mail',
-                                           'width' => 330,
+                                           'width' => 400,
                                            'type' => 'text',
+                                           'filter' => 't',
                                            'sql' => "username || '@' || name",
-                                           'specs' => "filterUI: 't'",
-                                           ),
-                             'forward' => array(
-                                           'name' => 'Forward',
-                                           'width' => 330,
-                                           'specs' => "filterUI: 't'",
-                                           ),
-                             'pw' => array(
-                                           'name' => 'Pass',
-                                           'width' => 40,
-                                           'specs' => "ClassName: 'aligncenter'",
-                                           'control' => "new Rico.TableColumn.checkbox('t', 'f',0,1)",
-                                           'sql' => 'password IS NOT NULL AND length(password) > 0',
                                            ),
                              'active' => array(
-                                           'name' => 'on',
+                                           'name' => 'aktiv',
                                            'width' => 40,
-                                           'specs' => "ClassName: 'aligncenter'",
-                                           'control' => "new Rico.TableColumn.checkbox(1, 0,0,1)",
+                                           'specs' => array('ClassName' => 'aligncenter has-checkbox'),
+                                           'control' => "new Rico.TableColumn.checkbox(1,0,0,1)",
                                            ),
                              ),
              'edit' => array(
                              'username' => array(
-                                           'name' => 'Usename',
+                                           'name' => 'Local Part',
                                            'type' => 'text',
                                            'size' => 21,
                                            'required' => true,
@@ -190,23 +87,8 @@ $mask = array(
                                            'option_empty' => '-- select --',
                                            'required' => true,
                                            ),
-                             'forward' => array(
-                                           'name' => 'Forward',
-                                           'type' => 'text',
-                                           'size' => 21,
-                                           'null' => true,
-                                           ),
-/*
-                             'password' => array(
-                                           'name' => 'Passwort',
-                                           'type' => 'passwd',
-                                           'size' => 21,
-                                           'null' => true,
-                                           'func' => 'passwd_encrypt',
-                                           ),
-*/
                              'active' => array(
-                                           'name' => 'aktiviert',
+                                           'name' => 'Mailbox aktiv',
                                            'type' => 'boolean',
                                            ),
                              'buttons' => array(
@@ -215,39 +97,4 @@ $mask = array(
                                            'sql' => false,
                                            ),
                              ),
-             'callbacks' => array(
-                                 'delete' => 'cb_delete',
-                                 'setpw' => 'cb_setpw',
-                                 ),
              );
-
-function passwd_encrypt($pw)
-{
-  return md5($pw);
-}
-
-function cb_delete()
-{
-  global $db;
-
-  $sql = sprintf("UPDATE vmail_user SET password = NULL WHERE id = %d", $_POST['id']);
-
-  $sth = $db->query($sql);
-
-  return true;
-}
-
-function cb_setpw()
-{
-  global $db;
-
-  $sql = sprintf("UPDATE vmail_user SET password = '%s' WHERE id = %d",
-                passwd_encrypt($_POST['passwd']),
-                $_POST['id']);
-
-  $sth = $db->query($sql);
-
-  return true;
-}
-
-?>
diff --git a/vmail.js b/vmail.js
new file mode 100644 (file)
index 0000000..1945205
--- /dev/null
+++ b/vmail.js
@@ -0,0 +1,286 @@
+var passwd_popup = false;
+var delete_popup = false;
+var discover_popup = false;
+
+function user_pre_save()
+{
+  if ($('#edit_username').val() == '*') {
+    alert("Fehler aufgetreten!\\nCatch-All-Einträge sind nur bei Weiterleitungen zulässig.");
+    $('#edit_username').focus();
+    return false;
+  }
+
+  return true;
+}
+
+function user_post_insert(data)
+{
+    Hallinta.showMsg("Bitte als nächstes ein Passwort setzen die Mailbox direkt mit Mailprogram aktivieren.", {timeout: 10});
+    ajax_request('VMail_User/CreateMailbox', 'id='+data.id);
+}
+
+function passwd_setpw()
+{
+    passwd_popup.closePopup();
+
+    if (!$('#pw_pass1').val().length || !$('#pw_pass2').val().length || $('#pw_pass1').val() != $('#pw_pass2').val()) {
+       alert("Die Passwörter stimmen nicht überein!");
+       return false;
+    }
+
+    var parms = 'id=' + $('#pw_id').val();
+    parms += '&passwd=' + $('#pw_pass1').val();
+
+    ajax_request('VMail_User/SetPassword', parms, function(data){
+       info('Passwort gespeichert');
+       Hallinta.showMsg('Neues Passwort gespeichert', {timeout: 3});
+    });
+
+    return false;
+}
+
+function passwd_set()
+{
+    if (!$('#edit_id').val().length) return false;
+
+    var centerDialog = false;
+
+    if (!passwd_popup) {
+       passwd_popup = new Rico.Window('<b>Neues Passwort setzen</b>', {zIndex: 100});
+       $(passwd_popup.contentDiv).html([
+           '<div class="form" style="margin-left: 10px; margin-right: 12px; padding-left: 10px; padding-bottom: 7px;">',
+           '<input type="hidden" name="pw_id" id="pw_id">',
+           '<label for="pw_email">E-Mail Adresse:</label><br>',
+           '<input type="text" name="pw_email" id="pw_email" size="23" readonly style="background:#eee;">',
+           '<div style="height: 5px;"></div>',
+           '<label for="pw_pass1">Neues Passwort:</label><br>',
+           '<input type="password" name="pw_pass1" id="pw_pass1" size="23">',
+           '<div style="height: 5px;"></div>',
+           '<label for="pw_pass2">erneut eingeben:</label><br>',
+           '<input type="password" name="pw_pass2" id="pw_pass2" size="23">',
+           '<div style="height: 5px;"></div>',
+           '<button onclick="return passwd_setpw()">Passwort setzen</button>',
+           '</div>',
+       ].join(''));
+       centerDialog = true;;
+    }
+
+    $('#pw_id').val($('#edit_id').val());
+    $('#pw_email').val($('#edit_username').val() + '@' + $('#edit_vmail_domain_id option[value="'+$('#edit_vmail_domain_id').val()+'"]').text());
+
+    $('#pw_pass1').val('');
+    $('#pw_pass2').val('');
+
+    if (centerDialog)
+        passwd_popup.centerPopup();
+    else
+        passwd_popup.openPopup();
+
+    $('#pw_pass1').focus();
+    return false;
+}
+
+function sieve_copy()
+{
+    ajax_request('VMail_User/SieveCopy', 'id=' + $('#edit_id').val(), function(data){
+       info('Skript kopiert');
+
+       if (typeof data == 'object' && data.need_identity === true)
+           Hallinta.showMsg('Sieve-Skript zu ' + SIEVEADDR + ' kopiert, neue Identität muß angelegt werden.',
+                            {timeout: 5});
+       else
+           Hallinta.showMsg('Sieve-Skript zu ' + SIEVEADDR + ' kopiert.', {timeout: 5});
+    });
+
+    return false;
+}
+
+function sieve_retrieve()
+{
+    ajax_request('VMail_User/SieveRestore', 'id=' + $('#edit_id').val(), function(data){
+       info('Skript zurückgeholt');
+       Hallinta.showMsg('Sieve-Skript von ' + SIEVEADDR + ' zurückgeholt.', {timeout: 5});
+    });
+
+    return false;
+}
+
+function user_pre_delete()
+{
+    Hallinta.showMsg("Löschen wird vorbereitet...");
+    ajax_request('VMail_User/DeleteReport', {id: $('#edit_id').val()}, function(data){
+           Hallinta.hideMsg();
+
+           if (!delete_popup) {
+               delete_popup = new Rico.Window('Delete Report', {zIndex: 100});
+
+               ajax_request('Template/Load', 'id=delete', function(html){
+                       $(delete_popup.contentDiv).html(html.data);
+                       $(delete_popup.contentDiv).find('#report').html(data.report);
+                       $(delete_popup.contentDiv).find('#btn_delete_really').attr('data-what', 'user');
+                       delete_popup.centerPopup();
+                   });
+           } else {
+               $(delete_popup.contentDiv).find('#report').html(data.report);
+               delete_popup.openPopup();
+           }
+       });
+
+    return false;
+}
+
+function alias_pre_delete()
+{
+    Hallinta.showMsg("Löschen wird vorbereitet...");
+    ajax_request('VMail_Alias/DeleteReport', {id: $('#edit_id').val()}, function(data){
+           Hallinta.hideMsg();
+
+           if (!delete_popup) {
+               delete_popup = new Rico.Window('Delete Report', {zIndex: 100});
+
+               ajax_request('Template/Load', 'id=delete', function(html){
+                       $(delete_popup.contentDiv).html(html.data);
+                       $(delete_popup.contentDiv).find('#report').html(data.report);
+                       $(delete_popup.contentDiv).find('#btn_delete_really').attr('data-what', 'alias');
+                       delete_popup.centerPopup();
+                   });
+           } else {
+               $(delete_popup.contentDiv).find('#report').html(data.report);
+               delete_popup.openPopup();
+           }
+       });
+
+    return false;
+}
+
+function delete_confirmation(obj)
+{
+    if ($(obj).attr('data-what') == 'user')
+       var backend = 'VMail_User/DeleteUser';
+    else if ($(obj).attr('data-what') == 'alias')
+       var backend = 'VMail_Alias/DeleteAlias';
+    else {
+       delete_popup.closePopup();
+       return Hallinta.showMsg('Unbekannte Betriebsart ' + $(obj).attr('data-what'), {timeout: 5});
+    }
+
+    ajax_request(backend, {id: $('#edit_id').val()}, function(data){
+       Hallinta.showMsg(data.text, {timeout: 3});
+       grid_update(Hallinta.grid);
+       delete_popup.closePopup();
+       Hallinta.editDialog.closePopup();
+    });
+}
+
+function discover_folder()
+{
+    if (!discover_popup) {
+       discover_popup = new Rico.Window('<b>Shared Folder</b>', {zIndex: 100});
+
+       ajax_request('Template/Load', 'id=folder', function(html){
+           $(discover_popup.contentDiv).html(html.data);
+
+           if ($('#discover_vmail_user_id option').length == 2) {
+               $('#discover_vmail_user_id').val($('#discover_vmail_user_id option:nth-child(2)').val());
+               discover_folderlist();
+           }
+
+           discover_popup.centerPopup();
+       });
+    } else {
+       discover_popup.openPopup();
+    }
+
+    return false;
+}
+
+function discover_folderlist(callback)
+{
+    if (!$('#discover_vmail_user_id').val().length) {
+       select_update('discover_folder', {}, 1);
+       return false;
+    }
+
+    ajax_request('VMail_User/GetFolderList', 'id='+$('#discover_vmail_user_id').val(), function(data){
+       console.log(data);
+       select_update('discover_folder', data.options, 1);
+       info('Folderliste geladen');
+       if (typeof callback == 'function')
+           callback();
+    });
+}
+
+function folder_add()
+{
+    if ($('#discover_folder').val().length) {
+       ajax_request('VMail_Folder/Add',
+                    'vmail_user_id='+$('#discover_vmail_user_id').val()+'&folder='+$('#discover_folder').val(),
+                    function(data){
+                        grid_update(Hallinta.grid);
+                    });
+    }
+    discover_popup.closePopup();
+}
+
+function folder_checkbox_change(checkbox, row, value)
+{
+    if (Hallinta.mainId === false) return;
+
+    var id = checkbox.liveGrid.columns[0].getValue(row);
+
+    ajax_request('VMail_Folder/Checkbox',
+                'id='+Hallinta.mainId+'&vmail_user_id='+id+'&value='+value);
+}
+
+function folder_checkbox_all(formdata)
+{
+    if (Hallinta.mainId === false) return;
+
+    ajax_request('VMail_Folder/CheckboxAll',
+                'id='+Hallinta.mainId+'&'+formdata);
+}
+
+function folder_del(id)
+{
+    var ok = confirm("Möchten Sie wirklich alle Abonnements löschen?");
+
+    if (!ok) return;
+
+    ajax_request('VMail_Folder/Delete',
+                'id='+id,
+                function(data){
+                    info('Shared Folder gelöscht');
+                    Hallinta.showMsg('Shared Folder und Abonnements gelöscht', {timeout: 3});
+                    grid_update(Hallinta.grid);
+                });
+    return;
+}
+
+function folder_new()
+{
+    var name = $('#new_folder').val();
+    if (name.indexOf('.') > -1) {
+       alert('Punkte sind in Foldernamen nicht erlaubt');
+       return;
+    }
+
+    if (name.indexOf('/') == 0) {
+       alert('Slash am Anfang eines Foldernamen nicht erlaubt');
+       return;
+    }
+
+    var userid = $('#discover_vmail_user_id').val();
+    if (!userid.length) {
+       alert('Bitte erst einen User auswählen');
+       return;
+    }
+
+    ajax_request('VMail_Folder/New',
+                'vmail_user_id='+userid+'&name='+encodeURIComponent(name),
+                function(data){
+                    Hallinta.showMsg('Folder angelegt, kann jetzt hinzugefügt werden', {timeout: 3});
+                    discover_folderlist(function(){
+                        $('#discover_folder').val(name);
+                    });
+                });
+}
index e2442d0..974a243 100644 (file)
--- a/vmail.sql
+++ b/vmail.sql
@@ -2,7 +2,8 @@ CREATE TABLE vmail_domain (
     id SERIAL,
     name character varying(100) NOT NULL,
     sys_user character varying(10) NOT NULL,
-    sys_edit timestamp without time zone NOT NULL
+    sys_edit timestamp without time zone NOT NULL,
+    UNIQUE(name)
 );
 
 CREATE UNIQUE INDEX vmail_domain_id ON vmail_domain USING btree (id);
@@ -12,15 +13,39 @@ CREATE TABLE vmail_user (
     vmail_domain_id integer NOT NULL,
     username character varying(50) NOT NULL,
     password character varying(50),
-    forward character varying(150),
     active integer DEFAULT 0 NOT NULL,
     sys_user character varying(10) NOT NULL,
-    sys_edit timestamp without time zone NOT NULL
+    sys_edit timestamp without time zone NOT NULL,
+    UNIQUE(vmail_domain_id,username)
 );
 
 CREATE UNIQUE INDEX vmail_user_id ON vmail_user USING btree (id);
 CREATE INDEX vmail_user_vmail_domain_id ON vmail_user USING btree (vmail_domain_id);
 
+-- ALTER TABLE ONLY vmail_user
+--     ADD CONSTRAINT vmail_user_vmail_domain_id_username_key UNIQUE (vmail_domain_id, username);
+
+CREATE TABLE vmail_alias (
+    id SERIAL,
+    vmail_domain_id integer NOT NULL,
+    username character varying(50) NOT NULL,
+    destination TEXT DEFAULT NULL,
+    islist integer DEFAULT 0 NOT NULL,
+    active integer DEFAULT 0 NOT NULL,
+    sys_user character varying(10) NOT NULL,
+    sys_edit timestamp without time zone NOT NULL,
+    UNIQUE(vmail_domain_id,username)
+);
+
+CREATE UNIQUE INDEX vmail_alias_id ON vmail_alias USING btree (id);
+CREATE INDEX vmail_alias_vmail_domain_id ON vmail_alias USING btree (vmail_domain_id);
+
+ALTER TABLE ONLY vmail_user
+    ADD CONSTRAINT "$1" FOREIGN KEY (vmail_domain_id) REFERENCES vmail_domain(id);
+
+ALTER TABLE ONLY vmail_alias
+    ADD CONSTRAINT "$1" FOREIGN KEY (vmail_domain_id) REFERENCES vmail_domain(id);
+
 REVOKE ALL ON TABLE vmail_user FROM PUBLIC;
 GRANT SELECT ON TABLE vmail_user TO "vmail";
 GRANT ALL ON TABLE vmail_user TO "hallinta";
@@ -28,6 +53,13 @@ GRANT ALL ON TABLE vmail_user TO "hallinta";
 REVOKE ALL ON SEQUENCE vmail_user_id_seq FROM PUBLIC;
 GRANT ALL ON SEQUENCE vmail_user_id_seq TO "hallinta";
 
+REVOKE ALL ON TABLE vmail_alias FROM PUBLIC;
+GRANT SELECT ON TABLE vmail_alias TO "vmail";
+GRANT ALL ON TABLE vmail_alias TO "hallinta";
+
+REVOKE ALL ON SEQUENCE vmail_alias_id_seq FROM PUBLIC;
+GRANT ALL ON SEQUENCE vmail_alias_id_seq TO "hallinta";
+
 REVOKE ALL ON TABLE vmail_domain FROM PUBLIC;
 GRANT SELECT ON TABLE vmail_domain TO "vmail";
 GRANT ALL ON TABLE vmail_domain TO "hallinta";
@@ -35,3 +67,104 @@ GRANT ALL ON TABLE vmail_domain TO "hallinta";
 REVOKE ALL ON SEQUENCE vmail_domain_id_seq FROM PUBLIC;
 GRANT ALL ON SEQUENCE vmail_domain_id_seq TO "hallinta";
 
+-- Shared Folders -------------------------------------------------------
+
+CREATE TABLE vmail_shares (
+  id SERIAL,
+  from_user varchar(100) not null,
+  to_user varchar(100) not null,
+  isset char(1) DEFAULT '1',    -- always '1' currently
+  primary key (from_user, to_user)
+);
+
+CREATE INDEX to_user ON vmail_shares (to_user); -- because we always search for to_user
+
+REVOKE ALL ON TABLE vmail_shares FROM PUBLIC;
+GRANT SELECT ON TABLE vmail_shares TO "vmail";
+GRANT ALL ON TABLE vmail_shares TO "hallinta";
+
+REVOKE ALL ON SEQUENCE vmail_shares_id_seq FROM PUBLIC;
+GRANT ALL ON SEQUENCE vmail_shares_id_seq TO "hallinta";
+
+
+CREATE TABLE vmail_anyone_shares (
+  id SERIAL,
+  from_user varchar(100) not null,
+  isset char(1) DEFAULT '1',    -- always '1' currently
+  primary key (from_user)
+);
+
+REVOKE ALL ON TABLE vmail_anyone_shares FROM PUBLIC;
+GRANT SELECT ON TABLE vmail_anyone_shares TO "vmail";
+GRANT ALL ON TABLE vmail_anyone_shares TO "hallinta";
+
+REVOKE ALL ON SEQUENCE vmail_anyone_shares_id_seq FROM PUBLIC;
+GRANT ALL ON SEQUENCE vmail_anyone_shares_id_seq TO "hallinta";
+
+
+CREATE TABLE vmail_folder (
+    id SERIAL,
+    vmail_user_id integer NOT NULL,
+    folder character varying(100) NOT NULL,
+    sys_user character varying(10) NOT NULL,
+    sys_edit timestamp without time zone NOT NULL,
+    UNIQUE(vmail_user_id,folder)
+);
+
+CREATE UNIQUE INDEX vmail_folder_id ON vmail_folder USING btree (id);
+CREATE INDEX vmail_folder_vmail_user_id ON vmail_folder USING btree (vmail_user_id);
+
+ALTER TABLE ONLY vmail_folder
+    ADD CONSTRAINT "$1" FOREIGN KEY (vmail_user_id) REFERENCES vmail_user(id);
+
+REVOKE ALL ON TABLE vmail_folder FROM PUBLIC;
+GRANT ALL ON TABLE vmail_folder TO "hallinta";
+
+REVOKE ALL ON SEQUENCE vmail_folder_id_seq FROM PUBLIC;
+GRANT ALL ON SEQUENCE vmail_folder_id_seq TO "hallinta";
+
+
+CREATE TABLE vmail_folder_user (
+    id SERIAL,
+    vmail_folder_id integer NOT NULL,
+    vmail_user_id integer NOT NULL,
+    sys_user character varying(10) NOT NULL,
+    sys_edit timestamp without time zone NOT NULL,
+    UNIQUE(vmail_folder_id,vmail_user_id)
+);
+
+CREATE UNIQUE INDEX vmail_folder_user_id ON vmail_folder_user USING btree (id);
+CREATE INDEX vmail_folder_user_vmail_folder_id ON vmail_folder_user USING btree (vmail_folder_id);
+CREATE INDEX vmail_folder_user_vmail_user_id ON vmail_folder_user USING btree (vmail_user_id);
+
+ALTER TABLE ONLY vmail_folder_user
+    ADD CONSTRAINT "$1" FOREIGN KEY (vmail_folder_id) REFERENCES vmail_folder(id);
+ALTER TABLE ONLY vmail_folder_user
+    ADD CONSTRAINT "$2" FOREIGN KEY (vmail_user_id) REFERENCES vmail_user(id);
+
+REVOKE ALL ON TABLE vmail_folder_user FROM PUBLIC;
+GRANT SELECT ON TABLE vmail_folder_user TO "vmail";
+GRANT ALL ON TABLE vmail_folder_user TO "hallinta";
+
+REVOKE ALL ON SEQUENCE vmail_folder_user_id_seq FROM PUBLIC;
+GRANT ALL ON SEQUENCE vmail_folder_user_id_seq TO "hallinta";
+
+----------------------------------------------------------------------
+
+CREATE TABLE vmail_filter_extension (
+    id SERIAL PRIMARY KEY,
+    extension varchar(10) NOT NULL,
+    sys_user character varying(10) NOT NULL,
+    sys_edit timestamp without time zone NOT NULL,
+    UNIQUE(extension)
+);
+
+CREATE UNIQUE INDEX vmail_filter_extension_id ON vmail_filter_extension USING btree (id);
+CREATE UNIQUE INDEX vmail_filter_extension_extension ON vmail_filter_extension USING btree (extension);
+
+REVOKE ALL ON TABLE vmail_filter_extension FROM PUBLIC;
+GRANT SELECT ON TABLE vmail_filter_extension TO "vmail";
+GRANT ALL ON TABLE vmail_filter_extension TO "hallinta";
+
+REVOKE ALL ON SEQUENCE vmail_filter_extension_id_seq FROM PUBLIC;
+GRANT ALL ON SEQUENCE vmail_filter_extension_id_seq TO "hallinta";