* @version 1.9
* @license GPL
* @link https://sourceforge.net/projects/rcubevacation/
* @todo See README.TXT
*/
class Virtual extends VacationDriver {
private $db, $domain, $domain_id, $goto = "";
private $db_user;
public function init() {
// Use the DSN from db.inc.php or a dedicated DSN defined in config.ini
if (empty($this->cfg['dsn'])) {
$this->db = $this->rcmail->db;
$dsn = MDB2::parseDSN($this->rcmail->config->get('db_dsnw'));
} else {
$this->db = new rcube_mdb2($this->cfg['dsn'], '', FALSE);
$this->db->db_connect('w');
$this->db->set_debug((bool) $this->rcmail->config->get('sql_debug'));
$dsn = MDB2::parseDSN($this->cfg['dsn']);
$this->db->set_debug(true);
}
// Save username for error handling
$this->db_user = $dsn['username'];
if (isset($this->cfg['createvacationconf']) && $this->cfg['createvacationconf']) {
$this->createVirtualConfig($dsn);
}
}
/*
* @return Array Values for the form
*/
public function _get() {
$vacArr = array("subject"=>"", "body"=>"");
// print_r($vacArr);
$fwdArr = $this->virtual_alias();
$sql = sprintf("SELECT subject,body,active FROM %s.vacation WHERE email='%s'",
$this->cfg['dbase'], Q($this->user->data['username']));
$res = $this->db->query($sql);
if ($error = $this->db->is_error()) {
raise_error(array('code' => 601, 'type' => 'db', 'file' => __FILE__,
'message' => "Vacation plugin: query on {$this->cfg['dbase']}.vacation failed. Check DSN and verify that SELECT privileges on {$this->cfg['dbase']}.vacation are granted to user '{$this->db_user}'.
Error message: " . $error), true, true);
}
if ($row = $this->db->fetch_assoc($res)) {
$vacArr['body'] = $row['body'];
$vacArr['subject'] = $row['subject'];
$vacArr['enabled'] = ($row['active'] == 1) && ($fwdArr['enabled'] == 1);
}
return array_merge($fwdArr, $vacArr);
}
/*
* @return boolean True on succes, false on failure
*/
public function setVacation() {
// If there is an existing entry in the vacation table, delete it.
// This also triggers the cascading delete on the vacation_notification, but's ok for now.
// We store since version 1.6 all data into one row.
$aliasArr = array();
// Sets class property
$this->domain_id = $this->domainLookup();
$sql = sprintf("UPDATE %s.vacation SET created=now(),active=0 WHERE email='%s'", $this->cfg['dbase'], Q($this->user->data['username']));
$this->db->query($sql);
$update = ($this->db->affected_rows() == 1);
// Delete the alias for the vacation transport (Postfix)
$sql = $this->translate($this->cfg['delete_query']);
$this->db->query($sql);
if ($error = $this->db->is_error()) {
if (strpos($error, "no such field")) {
$error = " Configure either domain_lookup_query or use %d in config.ini's delete_query rather than %i.
";
}
raise_error(array('code' => 601, 'type' => 'db', 'file' => __FILE__,
'message' => "Vacation plugin: Error while saving records to {$this->cfg['dbase']}.vacation table.
" . $error
), true, true);
}
// (Re)enable the vacation message and the vacation transport alias
if ($this->enable && $this->body != "" && $this->subject != "") {
if (!$update) {
$sql = "INSERT INTO {$this->cfg['dbase']}.vacation VALUES (?,?,?,'',?,NOW(),1)";
} else {
$sql = "UPDATE {$this->cfg['dbase']}.vacation SET email=?,subject=?,body=?,domain=?,active=1 WHERE email=?";
}
$this->db->query($sql, Q($this->user->data['username']), $this->subject, $this->body, $this->domain,Q($this->user->data['username']));
if ($error = $this->db->is_error()) {
if (strpos($error, "no such field")) {
$error = " Configure either domain_lookup_query or use \%d in config.ini's insert_query rather than \%i
";
}
raise_error(array('code' => 601, 'type' => 'db', 'file' => __FILE__,
'message' => "Vacation plugin: Error while saving records to {$this->cfg['dbase']}.vacation table.
" . $error
), true, true);
}
$aliasArr[] = '%g';
}
// Keep a copy of the mail if explicitly asked for or when using vacation
$always = (isset($this->cfg['always_keep_copy']) && $this->cfg['always_keep_copy']);
if ($this->keepcopy || in_array('%g', $aliasArr) || $always) {
$aliasArr[] = '%e';
}
// Set a forward
if ($this->forward != null) {
$aliasArr[] = '%f';
}
// Aliases are re-created if $sqlArr is not empty.
$sql = $this->translate($this->cfg['delete_query']);
$this->db->query($sql);
// One row to store all aliases
if (!empty($aliasArr)) {
$alias = join(",", $aliasArr);
$sql = str_replace('%g', $alias, $this->cfg['insert_query']);
$sql = $this->translate($sql);
$this->db->query($sql);
if ($error = $this->db->is_error()) {
raise_error(array('code' => 601, 'type' => 'db', 'file' => __FILE__,
'message' => "Vacation plugin: Error while executing {$this->cfg['insert_query']}
" . $error
), true, true);
}
}
return true;
}
/*
* @return string SQL query with substituted parameters
*/
private function translate($query) {
return str_replace(array('%e', '%d', '%i', '%g', '%f', '%m'),
array($this->user->data['username'], $this->domain, $this->domain_id,
Q($this->user->data['username']) . "@" . $this->cfg['transport'], $this->forward, $this->cfg['dbase']), $query);
}
// Sets %i. Lookup the domain_id based on the domainname. Returns the domainname if the query is empty
private function domainLookup() {
// Sets the domain
list($username, $this->domain) = explode("@", $this->user->get_username());
if (!empty($this->cfg['domain_lookup_query'])) {
$res = $this->db->query($this->translate($this->cfg['domain_lookup_query']));
if (!$row= $this->db->fetch_array($res)) {
raise_error(array('code' => 601, 'type' => 'db', 'file' => __FILE__,
'message' => "Vacation plugin: domain_lookup_query did not return any row. Check config.ini
" . $this->db->is_error()
), true, true);
}
return $row[0];
} else {
return $this->domain;
}
}
/*Creates configuration file for vacation.pl
*
* @param array dsn
* @return void
*/
private function createVirtualConfig(array $dsn) {
$virtual_config = "/etc/postfixadmin/";
if (!is_writeable($virtual_config)) {
raise_error(array('code' => 601, 'type' => 'php', 'file' => __FILE__,
'message' => "Vacation plugin: Cannot create {$virtual_config}vacation.conf . Check permissions.
"
), true, true);
}
// Fix for vacation.pl
if ($dsn['phptype'] == 'pgsql') {
$dsn['phptype'] = 'Pg';
}
$virtual_config .= "vacation.conf";
// Only recreate vacation.conf if config.ini has been modified since
if (!file_exists($virtual_config) || (filemtime("plugins/vacation/config.ini") > filemtime($virtual_config))) {
$config = sprintf("
\$db_type = '%s';
\$db_username = '%s';
\$db_password = '%s';
\$db_name = '%s';
\$vacation_domain = '%s';", $dsn['phptype'], $dsn['username'], $dsn['password'], $this->cfg['dbase'], $this->cfg['transport']);
file_put_contents($virtual_config, $config);
}
}
/*
Retrieves the localcopy and/or forward settings.
* @return array with virtual aliases
*/
private function virtual_alias() {
$forward = "";
$enabled = false;
$goto = Q($this->user->data['username']) . "@" . $this->cfg['transport'];
// Backwards compatiblity. Since >=1.6 this is no longer needed
$sql = str_replace("='%g'", "<>''", $this->cfg['select_query']);
$res = $this->db->query($this->translate($sql));
$rows = array();
while (list($row) = $this->db->fetch_array($res)) {
// Postfix accepts multiple aliases on 1 row as well as an alias per row
if (strpos($row, ",") !== false) {
$rows = explode(",", $row);
} else {
$rows[] = $row;
}
}
foreach ($rows as $row) {
// Source = destination means keep a local copy
if ($row == $this->user->data['username']) {
$keepcopy = true;
} else {
// Neither keepcopy or postfix transport means it's an a forward address
if ($row == $goto) {
$enabled = true;
} else {
// Support multi forwarding addresses
$forward .= $row . ",";
}
}
}
// Substr removes any trailing comma
return array("forward"=>substr($forward, 0, - 1), "keepcopy"=>$keepcopy, "enabled"=>$enabled);
}
// Destroy the database connection of our temporary database connection
public function __destruct() {
if (!empty($this->cfg['dsn']) && is_resource($this->db)) {
$this->db = null;
}
}
}
?>