Merge branch 'dev' into patch-1

This commit is contained in:
André Peters 2017-04-23 19:40:55 +02:00 committed by GitHub
commit 06274e1a2d
29 changed files with 1343 additions and 473 deletions

View File

@ -1,8 +1,8 @@
FROM debian:latest
FROM debian:stretch-slim
MAINTAINER https://m-ko.de Markus Kosmal <code@cnfg.io>
# Debian Base to use
ENV DEBIAN_VERSION jessie
ENV DEBIAN_VERSION stretch
# initial install of av daemon
RUN echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION main contrib non-free" > /etc/apt/sources.list && \
@ -13,15 +13,14 @@ RUN echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION main contrib non-fr
clamav-daemon \
clamav-freshclam \
libclamunrar7 \
wget && \
curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# initial update of av databases
RUN wget -O /var/lib/clamav/main.cvd http://db.local.clamav.net/main.cvd && \
wget -O /var/lib/clamav/daily.cvd http://db.local.clamav.net/daily.cvd && \
wget -O /var/lib/clamav/bytecode.cvd http://db.local.clamav.net/bytecode.cvd && \
chown clamav:clamav /var/lib/clamav/*.cvd
COPY dl_files.sh /dl_files.sh
RUN chmod +x /dl_files.sh
RUN /dl_files.sh
# permission juggling
RUN mkdir /var/run/clamav && \
@ -33,9 +32,6 @@ RUN sed -i 's/^Foreground .*$/Foreground true/g' /etc/clamav/clamd.conf && \
echo "TCPSocket 3310" >> /etc/clamav/clamd.conf && \
sed -i 's/^Foreground .*$/Foreground true/g' /etc/clamav/freshclam.conf
# volume provision
VOLUME ["/var/lib/clamav"]
# port provision
EXPOSE 3310

View File

@ -1,35 +1,7 @@
#!/bin/bash
# bootstrap clam av service and clam av database updater shell script
# presented by mko (Markus Kosmal<code@cnfg.io>)
set -m
trap "kill 0" SIGINT
# start clam service itself and the updater in background as daemon
freshclam -d &
clamd &
# recognize PIDs
pidlist=`jobs -p`
# initialize latest result var
latest_exit=0
# define shutdown helper
function shutdown() {
trap "" SUBS
for single in $pidlist; do
if ! kill -0 $pidlist 2>/dev/null; then
wait $pidlist
exitcode=$?
fi
done
kill $pidlist 2>/dev/null
}
# run shutdown
trap terminate SUBS
wait
# return received result
exit $latest_exit
sleep inf

View File

@ -0,0 +1,32 @@
#!/bin/bash
declare -a DB_MIRRORS=(
"switch.clamav.net"
"clamavdb.heanet.ie"
"clamav.iol.cz"
"clamav.univ-nantes.fr"
"clamav.easynet.fr"
"clamav.begi.net"
)
declare -a DB_MIRRORS=( $(shuf -e "${DB_MIRRORS[@]}") )
DB_FILES=(
"bytecode.cvd"
"daily.cvd"
"main.cvd"
)
for i in "${DB_MIRRORS[@]}"; do
for j in "${DB_FILES[@]}"; do
[[ -f "/var/lib/clamav/${j}" && -s "/var/lib/clamav/${j}" ]] && continue;
if [[ $(curl -o /dev/null --connect-timeout 1 \
--max-time 1 \
--silent \
--head \
--write-out "%{http_code}\n" "${i}/${j}") == 200 ]]; then
curl "${i}/${j}" -o "/var/lib/clamav/${j}" -#
fi
done
done
chown clamav:clamav /var/lib/clamav/*.cvd

View File

@ -23,6 +23,7 @@ RUN apt-get install -y --no-install-recommends supervisor \
gnupg \
python-gpgme \
sudo \
curl \
dirmngr
RUN addgroup --system --gid 600 zeyple
@ -34,6 +35,7 @@ COPY zeyple.py /usr/local/bin/zeyple.py
COPY zeyple.conf /etc/zeyple.conf
COPY supervisord.conf /etc/supervisor/supervisord.conf
COPY postfix.sh /opt/postfix.sh
COPY whitelist_forwardinghosts.sh /usr/local/bin/whitelist_forwardinghosts.sh
EXPOSE 588

View File

@ -0,0 +1,12 @@
#!/bin/bash
while read QUERY; do
QUERY=($QUERY)
if [ "${QUERY[0]}" != "get" ]; then
echo "500 dunno"
continue
fi
result=$(curl -s http://nginx:8081/forwardinghosts.php?host=${QUERY[1]})
logger -t whitelist_forwardinghosts -p mail.info "Look up ${QUERY[1]} on whitelist, result $result"
echo ${result}
done

View File

@ -24,7 +24,7 @@ milter_default_action = accept
milter_protocol = 6
minimal_backoff_time = 300s
plaintext_reject_code = 550
postscreen_access_list = permit_mynetworks, cidr:/opt/postfix/conf/postscreen_access.cidr
postscreen_access_list = permit_mynetworks, cidr:/opt/postfix/conf/postscreen_access.cidr, tcp:127.0.0.1:10027
postscreen_bare_newline_enable = no
postscreen_blacklist_action = drop
postscreen_cache_cleanup_interval = 24h

View File

@ -55,3 +55,5 @@ zeyple unix - n n - - pipe
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks=127.0.0.0/8
-o smtpd_authorized_xforward_hosts=127.0.0.0/8
127.0.0.1:10027 inet n n n - 0 spawn user=nobody argv=/usr/local/bin/whitelist_forwardinghosts.sh

View File

@ -0,0 +1,56 @@
<?php
header('Content-Type: text/plain');
require_once "vars.inc.php";
ini_set('error_reporting', 0);
function in_net($addr, $net)
{
$net = explode('/', $net);
if (count($net) > 1)
$mask = $net[1];
$net = inet_pton($net[0]);
$addr = inet_pton($addr);
$length = strlen($net); // 4 for IPv4, 16 for IPv6
if (strlen($net) != strlen($addr))
return FALSE;
if (!isset($mask))
$mask = $length * 8;
$addr_bin = '';
$net_bin = '';
for ($i = 0; $i < $length; ++$i)
{
$addr_bin .= str_pad(decbin(ord(substr($addr, $i, $i+1))), 8, '0', STR_PAD_LEFT);
$net_bin .= str_pad(decbin(ord(substr($net, $i, $i+1))), 8, '0', STR_PAD_LEFT);
}
return substr($addr_bin, 0, $mask) == substr($net_bin, 0, $mask);
}
$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
$stmt = $pdo->query("SELECT host FROM `forwarding_hosts`");
$networks = $stmt->fetchAll(PDO::FETCH_COLUMN);
foreach ($networks as $network)
{
if (in_net($_GET['host'], $network))
{
echo '200 permit';
exit;
}
}
echo '200 dunno';
}
catch (PDOException $e) {
echo '200 dunno';
exit;
}
?>

View File

@ -32,6 +32,35 @@ catch (PDOException $e) {
?>
settings {
<?php
try {
$stmt = $pdo->query("SELECT `host` FROM `forwarding_hosts`");
$rows = $stmt->fetchAll(PDO::FETCH_COLUMN);
}
catch (PDOException $e) {
$rows = array();
}
if ($rows)
{
?>
whitelist_forwarding_hosts {
priority = high;
<?php
foreach ($rows as $host) {
echo "\t\t" . 'ip = "' . $host . '";' . "\n";
}
?>
apply "default" {
actions {
reject = 999.9;
}
}
symbols [
"WHITELIST_FORWARDING_HOST"
]
}
<?php
}
$stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'highspamlevel' OR `option` = 'lowspamlevel'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
@ -207,8 +236,11 @@ while ($row = array_shift($rows)) {
}
?>
apply "default" {
MAILCOW_MOO = -999.0;
MAILCOW_WHITE = -999.0;
}
symbols [
"MAILCOW_WHITE"
]
}
<?php
}
@ -302,10 +334,13 @@ while ($row = array_shift($rows)) {
}
?>
apply "default" {
MAILCOW_MOO = 999.0;
MAILCOW_BLACK = 999.0;
}
symbols [
"MAILCOW_BLACK"
]
}
<?php
}
?>
}
}

View File

@ -0,0 +1,8 @@
clamav {
attachments_only = false;
max_size = 20000000;
symbol = "CLAM_VIRUS";
type = "clamav";
log_clean = true;
servers = "clamd:3310";
}

View File

@ -0,0 +1,7 @@
rules {
DKIM_FAIL {
action = "add header";
expression = "R_DKIM_REJECT & !MAILLIST & !MAILCOW_WHITE & !MAILCOW_BLACK";
require_action = ["no action", "greylist"];
}
}

View File

@ -7,10 +7,6 @@ rspamd_config.MAILCOW_AUTH = {
end
}
rspamd_config.MAILCOW_MOO = function (task)
return true
end
modify_subject_map = rspamd_config:add_map({
url = 'http://172.22.1.251:8081/tags.php',
type = 'map',

View File

@ -353,7 +353,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] ==
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label><input type="checkbox" name="delete1" checked> <?=$lang['add']['delete1'];?></label>
<label><input type="checkbox" name="delete1"> <?=$lang['add']['delete1'];?></label>
</div>
</div>
</div>

View File

@ -183,9 +183,11 @@ $tfa_data = get_tfa();
</div>
<h4><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> <?=$lang['admin']['configuration'];?></h4>
<div class="panel-group" id="accordion_access">
<div class="panel panel-default">
<div class="panel-heading"><?=$lang['admin']['dkim_keys'];?></div>
<div id="collapseDKIM" class="panel-collapse">
<div class="panel-body">
<p style="margin-bottom:40px"><?=$lang['admin']['dkim_key_hint'];?></p>
<?php
@ -297,7 +299,72 @@ $tfa_data = get_tfa();
</form>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><?=$lang['admin']['forwarding_hosts'];?></div>
<div class="panel-body">
<p style="margin-bottom:40px"><?=$lang['admin']['forwarding_hosts_hint'];?></p>
<form method="post">
<div class="table-responsive">
<table class="table table-striped" id="forwardinghoststable">
<thead>
<tr>
<th style="min-width: 100px;"><?=$lang['edit']['host'];?></th>
<th style="min-width: 100px;"><?=$lang['edit']['source'];?></th>
<th style="text-align: right; min-width: 200px;"><?=$lang['admin']['action'];?></th>
</tr>
</thead>
<tbody>
<?php
$forwarding_hosts = get_forwarding_hosts();
if ($forwarding_hosts) {
foreach ($forwarding_hosts as $host) {
$source = $host->source;
$host = $host->host;
?>
<tr id="data">
<td><?=htmlspecialchars(strtolower($host));?></td>
<td><?=htmlspecialchars(strtolower($source));?></td>
<td style="text-align: right;">
<div class="btn-group">
<a href="delete.php?forwardinghost=<?=$host;?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['admin']['remove'];?></a>
</div>
</td>
</td>
</tr>
<?php
}
} else {
?>
<tr id="no-data"><td colspan="4" style="text-align: center; font-style: italic;"><?=$lang['admin']['no_record'];?></td></tr>
<?php
}
?>
</tbody>
</table>
</div>
</form>
<legend><?=$lang['admin']['add_forwarding_host'];?></legend>
<p class="help-block"><?=$lang['admin']['forwarding_hosts_add_hint'];?></p>
<form class="form-horizontal" role="form" method="post">
<div class="form-group">
<label class="control-label col-sm-2" for="hostname"><?=$lang['edit']['host'];?>:</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="hostname" id="hostname" required>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" name="add_forwarding_host" class="btn btn-default"><?=$lang['admin']['add'];?></button>
</div>
</div>
</form>
</div>
</div>
</div>
</div> <!-- /container -->
<script type='text/javascript'>
<?php

View File

@ -105,6 +105,23 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
</form>
<?php
}
// DELETE FORWARDING HOST
elseif (isset($_GET["forwardinghost"]) &&
!empty($_GET["forwardinghost"]) &&
$_SESSION['mailcow_cc_role'] == "admin") {
$host = $_GET["forwardinghost"];
?>
<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_forwardinghost_warning'], htmlspecialchars($_GET["forwardinghost"]));?></div>
<form class="form-horizontal" role="form" method="post" action="/admin.php">
<input type="hidden" name="forwardinghost" value="<?=htmlspecialchars($host);?>">
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<button type="submit" name="delete_forwarding_host" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
</div>
</div>
</form>
<?php
}
// DELETE MAILBOX
elseif (isset($_GET["mailbox"]) &&
filter_var($_GET["mailbox"], FILTER_VALIDATE_EMAIL) &&

View File

@ -62,69 +62,6 @@ function hasMailboxObjectAccess($username, $role, $object) {
}
return false;
}
function init_db_schema() {
// This will be much better in future releases...
global $pdo;
try {
$stmt = $pdo->prepare("SELECT NULL FROM `admin`, `imapsync`, `tfa`");
$stmt->execute();
}
catch (Exception $e) {
$lines = file('/web/inc/init.sql');
$data = '';
foreach ($lines as $line) {
if (substr($line, 0, 2) == '--' || $line == '') {
continue;
}
$data .= $line;
if (substr(trim($line), -1, 1) == ';') {
$pdo->query($data);
$data = '';
}
}
// Create index if not exists
$stmt = $pdo->query("SHOW INDEX FROM sogo_acl WHERE KEY_NAME = 'sogo_acl_c_folder_id_idx'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results == 0) {
$pdo->query("CREATE INDEX sogo_acl_c_folder_id_idx ON sogo_acl(c_folder_id)");
}
$stmt = $pdo->query("SHOW INDEX FROM sogo_acl WHERE KEY_NAME = 'sogo_acl_c_uid_idx'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results == 0) {
$pdo->query("CREATE INDEX sogo_acl_c_uid_idx ON sogo_acl(c_uid)");
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => 'Database initialization completed.'
);
}
// Add newly added columns
$stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'kind'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results == 0) {
$pdo->query("ALTER TABLE `mailbox` ADD `kind` VARCHAR(100) NOT NULL DEFAULT ''");
}
$stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'multiple_bookings'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results == 0) {
$pdo->query("ALTER TABLE `mailbox` ADD `multiple_bookings` tinyint(1) NOT NULL DEFAULT '0'");
}
$stmt = $pdo->query("SHOW COLUMNS FROM `imapsync` LIKE 'delete1'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results == 0) {
$pdo->query("ALTER TABLE `imapsync` ADD `delete1` tinyint(1) NOT NULL DEFAULT '0'");
}
$stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'wants_tagged_subject'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results == 0) {
$pdo->query("ALTER TABLE `mailbox` ADD `wants_tagged_subject` tinyint(1) NOT NULL DEFAULT '0'");
}
$stmt = $pdo->query("SHOW COLUMNS FROM `tfa` LIKE 'key_id'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results == 0) {
$pdo->query("ALTER TABLE `tfa` ADD `key_id` VARCHAR(255) DEFAULT 'unidentified'");
}
}
function verify_ssha256($hash, $password) {
// Remove tag if any
$hash = ltrim($hash, '{SSHA256}');
@ -287,13 +224,11 @@ function edit_admin_account($postarray) {
$password_hashed = hash_password($password);
try {
$stmt = $pdo->prepare("UPDATE `admin` SET
`modified` = :modified,
`password` = :password_hashed,
`username` = :username1
WHERE `username` = :username2");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':modified' => date('Y-m-d H:i:s'),
':username1' => $username,
':username2' => $username_now
));
@ -309,12 +244,10 @@ function edit_admin_account($postarray) {
else {
try {
$stmt = $pdo->prepare("UPDATE `admin` SET
`modified` = :modified,
`username` = :username1
WHERE `username` = :username2");
$stmt->execute(array(
':username1' => $username,
':modified' => date('Y-m-d H:i:s'),
':username2' => $username_now
));
}
@ -608,10 +541,9 @@ function edit_user_account($postarray) {
}
$password_hashed = hash_password($password_new);
try {
$stmt = $pdo->prepare("UPDATE `mailbox` SET `modified` = :modified, `password` = :password_hashed WHERE `username` = :username");
$stmt = $pdo->prepare("UPDATE `mailbox` SET `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':modified' => date('Y-m-d H:i:s'),
':username' => $username
));
}
@ -1633,13 +1565,11 @@ function add_domain_admin($postarray) {
}
}
try {
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `created`, `modified`, `active`)
VALUES (:username, :password_hashed, '0', :created, :modified, :active)");
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
VALUES (:username, :password_hashed, '0', :active)");
$stmt->execute(array(
':username' => $username,
':password_hashed' => $password_hashed,
':created' => date('Y-m-d H:i:s'),
':modified' => date('Y-m-d H:i:s'),
':active' => $active
));
}
@ -2228,12 +2158,11 @@ function edit_domain_admin($postarray) {
}
$password_hashed = hash_password($password);
try {
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `modified` = :modified, `active` = :active, `password` = :password_hashed WHERE `username` = :username2");
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `active` = :active, `password` = :password_hashed WHERE `username` = :username2");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username1' => $username,
':username2' => $username_now,
':modified' => date('Y-m-d H:i:s'),
':active' => $active
));
if (isset($postarray['disable_tfa'])) {
@ -2255,11 +2184,10 @@ function edit_domain_admin($postarray) {
}
else {
try {
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `modified` = :modified, `active` = :active WHERE `username` = :username2");
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `active` = :active WHERE `username` = :username2");
$stmt->execute(array(
':username1' => $username,
':username2' => $username_now,
':modified' => date('Y-m-d H:i:s'),
':active' => $active
));
if (isset($postarray['disable_tfa'])) {
@ -2321,10 +2249,9 @@ function edit_domain_admin($postarray) {
}
$password_hashed = hash_password($password_new);
try {
$stmt = $pdo->prepare("UPDATE `admin` SET `modified` = :modified, `password` = :password_hashed WHERE `username` = :username");
$stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':modified' => date('Y-m-d H:i:s'),
':username' => $username
));
}
@ -2356,7 +2283,7 @@ function get_admin_details() {
return false;
}
try {
$stmt = $pdo->prepare("SELECT `username`, `modified`, `created` FROM `admin`WHERE `superadmin`='1' AND active='1'");
$stmt = $pdo->prepare("SELECT `username`, `modified`, `created` FROM `admin` WHERE `superadmin`='1' AND active='1'");
$stmt->execute();
$data = $stmt->fetch(PDO::FETCH_ASSOC);
}
@ -2601,8 +2528,8 @@ function mailbox_add_domain($postarray) {
}
try {
$stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `maxquota`, `quota`, `transport`, `backupmx`, `created`, `modified`, `active`, `relay_all_recipients`)
VALUES (:domain, :description, :aliases, :mailboxes, :maxquota, :quota, 'virtual', :backupmx, :created, :modified, :active, :relay_all_recipients)");
$stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `maxquota`, `quota`, `transport`, `backupmx`, `active`, `relay_all_recipients`)
VALUES (:domain, :description, :aliases, :mailboxes, :maxquota, :quota, 'virtual', :backupmx, :active, :relay_all_recipients)");
$stmt->execute(array(
':domain' => $domain,
':description' => $description,
@ -2612,8 +2539,6 @@ function mailbox_add_domain($postarray) {
':quota' => $quota,
':backupmx' => $backupmx,
':active' => $active,
':created' => date('Y-m-d H:i:s'),
':modified' => date('Y-m-d H:i:s'),
':relay_all_recipients' => $relay_all_recipients
));
$_SESSION['return'] = array(
@ -2789,16 +2714,14 @@ function mailbox_add_alias($postarray) {
$goto = implode(",", $gotos);
try {
$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `created`, `modified`, `active`)
VALUES (:address, :goto, :domain, :created, :modified, :active)");
$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `active`)
VALUES (:address, :goto, :domain, :active)");
if (!filter_var($address, FILTER_VALIDATE_EMAIL) === true) {
$stmt->execute(array(
':address' => '@'.$domain,
':goto' => $goto,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':modified' => date('Y-m-d H:i:s'),
':active' => $active
));
}
@ -2807,8 +2730,6 @@ function mailbox_add_alias($postarray) {
':address' => $address,
':goto' => $goto,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':modified' => date('Y-m-d H:i:s'),
':active' => $active
));
}
@ -2909,13 +2830,11 @@ function mailbox_add_alias_domain($postarray) {
}
try {
$stmt = $pdo->prepare("INSERT INTO `alias_domain` (`alias_domain`, `target_domain`, `created`, `modified`, `active`)
VALUES (:alias_domain, :target_domain, :created, :modified, :active)");
$stmt = $pdo->prepare("INSERT INTO `alias_domain` (`alias_domain`, `target_domain`, `active`)
VALUES (:alias_domain, :target_domain, :active)");
$stmt->execute(array(
':alias_domain' => $alias_domain,
':target_domain' => $target_domain,
':created' => date('Y-m-d H:i:s'),
':modified' => date('Y-m-d H:i:s'),
':active' => $active
));
$_SESSION['return'] = array(
@ -3118,8 +3037,8 @@ function mailbox_add_mailbox($postarray) {
}
try {
$stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `created`, `modified`, `active`)
VALUES (:username, :password_hashed, :name, :maildir, :quota_b, :local_part, :domain, :created, :modified, :active)");
$stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `active`)
VALUES (:username, :password_hashed, :name, :maildir, :quota_b, :local_part, :domain, :active)");
$stmt->execute(array(
':username' => $username,
':password_hashed' => $password_hashed,
@ -3128,8 +3047,6 @@ function mailbox_add_mailbox($postarray) {
':quota_b' => $quota_b,
':local_part' => $local_part,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':modified' => date('Y-m-d H:i:s'),
':active' => $active
));
@ -3137,14 +3054,12 @@ function mailbox_add_mailbox($postarray) {
VALUES (:username, '0', '0')");
$stmt->execute(array(':username' => $username));
$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `created`, `modified`, `active`)
VALUES (:username1, :username2, :domain, :created, :modified, :active)");
$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `active`)
VALUES (:username1, :username2, :domain, :active)");
$stmt->execute(array(
':username1' => $username,
':username2' => $username,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':modified' => date('Y-m-d H:i:s'),
':active' => $active
));
@ -3274,15 +3189,13 @@ function mailbox_add_resource($postarray) {
}
try {
$stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `created`, `modified`, `active`, `multiple_bookings`, `kind`)
VALUES (:name, 'RESOURCE', :description, 'RESOURCE', 0, :local_part, :domain, :created, :modified, :active, :multiple_bookings, :kind)");
$stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `active`, `multiple_bookings`, `kind`)
VALUES (:name, 'RESOURCE', :description, 'RESOURCE', 0, :local_part, :domain, :active, :multiple_bookings, :kind)");
$stmt->execute(array(
':name' => $name,
':description' => $description,
':local_part' => $local_part,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':modified' => date('Y-m-d H:i:s'),
':active' => $active,
':kind' => $kind,
':multiple_bookings' => $multiple_bookings
@ -3373,12 +3286,10 @@ function mailbox_edit_alias_domain($postarray) {
try {
$stmt = $pdo->prepare("UPDATE `alias_domain` SET
`alias_domain` = :alias_domain,
`active` = :active,
`modified` = :modified
`active` = :active
WHERE `alias_domain` = :alias_domain_now");
$stmt->execute(array(
':alias_domain' => $alias_domain,
':modified' => date('Y-m-d H:i:s'),
':alias_domain_now' => $alias_domain_now,
':active' => $active
));
@ -3454,14 +3365,12 @@ function mailbox_edit_alias($postarray) {
try {
$stmt = $pdo->prepare("UPDATE `alias` SET
`goto` = :goto,
`active`= :active,
`modified` = :modified
`active`= :active
WHERE `address` = :address");
$stmt->execute(array(
':goto' => $goto,
':active' => $active,
':address' => $address,
':modified' => date('Y-m-d H:i:s'),
':address' => $address
));
$_SESSION['return'] = array(
'type' => 'success',
@ -3506,11 +3415,9 @@ function mailbox_edit_domain($postarray) {
isset($postarray['active']) ? $active = '1' : $active = '0';
try {
$stmt = $pdo->prepare("UPDATE `domain` SET
`modified`= :modified,
`description` = :description
WHERE `domain` = :domain");
$stmt->execute(array(
':modified' => date('Y-m-d H:i:s'),
':description' => $description,
':domain' => $domain
));
@ -3614,7 +3521,6 @@ function mailbox_edit_domain($postarray) {
}
try {
$stmt = $pdo->prepare("UPDATE `domain` SET
`modified`= :modified,
`relay_all_recipients` = :relay_all_recipients,
`backupmx` = :backupmx,
`active` = :active,
@ -3632,7 +3538,6 @@ function mailbox_edit_domain($postarray) {
':maxquota' => $maxquota,
':mailboxes' => $mailboxes,
':aliases' => $aliases,
':modified' => date('Y-m-d H:i:s'),
':description' => $description,
':domain' => $domain
));
@ -3844,23 +3749,19 @@ function mailbox_edit_mailbox($postarray) {
$password_hashed = hash_password($password);
try {
$stmt = $pdo->prepare("UPDATE `alias` SET
`modified` = :modified,
`active` = :active
WHERE `address` = :address");
$stmt->execute(array(
':address' => $username,
':modified' => date('Y-m-d H:i:s'),
':active' => $active
));
$stmt = $pdo->prepare("UPDATE `mailbox` SET
`modified` = :modified,
`active` = :active,
`password` = :password_hashed,
`name`= :name,
`quota` = :quota_b
WHERE `username` = :username");
$stmt->execute(array(
':modified' => date('Y-m-d H:i:s'),
':password_hashed' => $password_hashed,
':active' => $active,
':name' => $name,
@ -3883,23 +3784,19 @@ function mailbox_edit_mailbox($postarray) {
}
try {
$stmt = $pdo->prepare("UPDATE `alias` SET
`modified` = :modified,
`active` = :active
WHERE `address` = :address");
$stmt->execute(array(
':address' => $username,
':modified' => date('Y-m-d H:i:s'),
':active' => $active
));
$stmt = $pdo->prepare("UPDATE `mailbox` SET
`modified` = :modified,
`active` = :active,
`name`= :name,
`quota` = :quota_b
WHERE `username` = :username");
$stmt->execute(array(
':active' => $active,
':modified' => date('Y-m-d H:i:s'),
':name' => $name,
':quota_b' => $quota_b,
':username' => $username
@ -3962,7 +3859,6 @@ function mailbox_edit_resource($postarray) {
try {
$stmt = $pdo->prepare("UPDATE `mailbox` SET
`modified` = :modified,
`active` = :active,
`name`= :description,
`kind`= :kind,
@ -3970,7 +3866,6 @@ function mailbox_edit_resource($postarray) {
WHERE `username` = :name");
$stmt->execute(array(
':active' => $active,
':modified' => date('Y-m-d H:i:s'),
':description' => $description,
':multiple_bookings' => $multiple_bookings,
':kind' => $kind,
@ -4796,12 +4691,10 @@ function mailbox_delete_mailbox($postarray) {
}
$gotos_rebuild = implode(',', $goto_exploded);
$stmt = $pdo->prepare("UPDATE `alias` SET
`goto` = :goto,
`modified` = :modified,
`goto` = :goto
WHERE `address` = :address");
$stmt->execute(array(
':goto' => $gotos_rebuild,
':modified' => date('Y-m-d H:i:s'),
':address' => $gotos['address']
));
}
@ -5039,4 +4932,93 @@ function get_u2f_registrations($username) {
$sel->execute(array($username));
return $sel->fetchAll(PDO::FETCH_OBJ);
}
function get_forwarding_hosts() {
global $pdo;
$sel = $pdo->prepare("SELECT host, source FROM `forwarding_hosts`");
$sel->execute();
return $sel->fetchAll(PDO::FETCH_OBJ);
}
function add_forwarding_host($postarray) {
require_once 'spf.inc.php';
global $pdo;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
$source = $postarray['hostname'];
$host = $postarray['hostname'];
$hosts = array();
if (preg_match('/^[0-9a-fA-F:\/]+$/', $host)) { // IPv6 address
$hosts = array($host);
}
elseif (preg_match('/^[0-9\.\/]+$/', $host)) { // IPv4 address
$hosts = array($host);
}
else {
$hosts = get_outgoing_hosts_best_guess($host);
}
if (!$hosts)
{
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Invalid host specified: '. htmlspecialchars($host)
);
return false;
}
foreach ($hosts as $host) {
if ($source == $host)
$source = '';
try {
$stmt = $pdo->prepare("INSERT IGNORE INTO `forwarding_hosts` (`host`, `source`) VALUES (:host, :source)");
$stmt->execute(array(
':host' => $host,
':source' => $source,
));
}
catch (PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
return false;
}
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => sprintf($lang['success']['forwarding_host_added'], htmlspecialchars(implode(', ', $hosts)))
);
}
function delete_forwarding_host($postarray) {
global $pdo;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
$host = $postarray['forwardinghost'];
try {
$stmt = $pdo->prepare("DELETE FROM `forwarding_hosts` WHERE `host` = :host");
$stmt->execute(array(
':host' => $host,
));
}
catch (PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
return false;
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => sprintf($lang['success']['forwarding_host_removed'], htmlspecialchars($host))
);
}
?>

View File

@ -1,281 +0,0 @@
CREATE TABLE IF NOT EXISTS `admin` (
`username` VARCHAR(255) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`superadmin` TINYINT(1) NOT NULL DEFAULT '0',
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`active` TINYINT(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `alias` (
`address` VARCHAR(255) NOT NULL,
`goto` TEXT NOT NULL,
`domain` VARCHAR(255) NOT NULL,
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`active` TINYINT(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`address`),
KEY `domain` (`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `sender_acl` (
`logged_in_as` VARCHAR(255) NOT NULL,
`send_as` VARCHAR(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `spamalias` (
`address` VARCHAR(255) NOT NULL,
`goto` TEXT NOT NULL,
`validity` INT(11) NOT NULL,
PRIMARY KEY (`address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `alias_domain` (
`alias_domain` VARCHAR(255) NOT NULL,
`target_domain` VARCHAR(255) NOT NULL,
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`active` TINYINT(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`alias_domain`),
KEY `active` (`active`),
KEY `target_domain` (`target_domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `domain` (
`domain` VARCHAR(255) NOT NULL,
`description` VARCHAR(255),
`aliases` INT(10) NOT NULL DEFAULT '0',
`mailboxes` INT(10) NOT NULL DEFAULT '0',
`maxquota` BIGINT(20) NOT NULL DEFAULT '0',
`quota` BIGINT(20) NOT NULL DEFAULT '0',
`transport` VARCHAR(255) NOT NULL,
`backupmx` TINYINT(1) NOT NULL DEFAULT '0',
`relay_all_recipients` TINYINT(1) NOT NULL DEFAULT '0',
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`active` TINYINT(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `domain_admins` (
`username` VARCHAR(255) NOT NULL,
`domain` VARCHAR(255) NOT NULL,
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`active` TINYINT(1) NOT NULL DEFAULT '1',
KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `mailbox` (
`username` VARCHAR(255) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`name` VARCHAR(255),
`maildir` VARCHAR(255) NOT NULL,
`quota` BIGINT(20) NOT NULL DEFAULT '0',
`local_part` VARCHAR(255) NOT NULL,
`domain` VARCHAR(255) NOT NULL,
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
`tls_enforce_in` TINYINT(1) NOT NULL DEFAULT '0',
`tls_enforce_out` TINYINT(1) NOT NULL DEFAULT '0',
`kind` VARCHAR(100) NOT NULL DEFAULT '',
`multiple_bookings` TINYINT(1) NOT NULL DEFAULT '0',
`wants_tagged_subject` TINYINT(1) NOT NULL DEFAULT '0',
`active` TINYINT(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`username`),
KEY `domain` (`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `quota2` (
`username` VARCHAR(100) NOT NULL,
`bytes` BIGINT(20) NOT NULL DEFAULT '0',
`messages` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `filterconf` (
`object` VARCHAR(100) NOT NULL DEFAULT '',
`option` VARCHAR(50) NOT NULL DEFAULT '',
`value` VARCHAR(100) NOT NULL DEFAULT '',
`prefid` INT(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`prefid`),
KEY `object` (`object`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `imapsync` (
`id` INT NOT NULL AUTO_INCREMENT,
`user2` VARCHAR(255) NOT NULL,
`host1` VARCHAR(255) NOT NULL,
`authmech1` ENUM('PLAIN','LOGIN','CRAM-MD5') DEFAULT 'PLAIN',
`regextrans2` VARCHAR(255) DEFAULT '',
`authmd51` TINYINT(1) NOT NULL DEFAULT 0,
`domain2` VARCHAR(255) NOT NULL DEFAULT '',
`subfolder2` VARCHAR(255) NOT NULL DEFAULT '',
`user1` VARCHAR(255) NOT NULL,
`password1` VARCHAR(255) NOT NULL,
`exclude` VARCHAR(500) NOT NULL DEFAULT '',
`maxage` SMALLINT NOT NULL DEFAULT '0',
`mins_interval` VARCHAR(50) NOT NULL,
`port1` SMALLINT NOT NULL,
`enc1` ENUM('TLS','SSL','PLAIN') DEFAULT 'TLS',
`delete2duplicates` TINYINT(1) NOT NULL DEFAULT '1',
`returned_text` TEXT,
`last_run` TIMESTAMP NULL DEFAULT NULL,
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`active` TINYINT(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS `tfa` (
`id` INT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(255) NOT NULL,
`authmech` ENUM('yubi_otp', 'u2f', 'hotp', 'totp'),
`secret` VARCHAR(255) DEFAULT NULL,
`keyHandle` VARCHAR(255) DEFAULT NULL,
`publicKey` VARCHAR(255) DEFAULT NULL,
`counter` INT NOT NULL DEFAULT '0',
`certificate` TEXT,
`active` TINYINT(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
DROP VIEW IF EXISTS grouped_mail_aliases;
DROP VIEW IF EXISTS grouped_sender_acl;
DROP VIEW IF EXISTS grouped_domain_alias_address;
CREATE VIEW grouped_mail_aliases (username, aliases) AS
SELECT goto, IFNULL(GROUP_CONCAT(address SEPARATOR ' '), '') AS address FROM alias
WHERE address!=goto
AND active = '1'
AND address NOT LIKE '@%'
GROUP BY goto;
CREATE VIEW grouped_sender_acl (username, send_as) AS
SELECT logged_in_as, IFNULL(GROUP_CONCAT(send_as SEPARATOR ' '), '') AS send_as FROM sender_acl
WHERE send_as NOT LIKE '@%'
GROUP BY logged_in_as;
CREATE VIEW grouped_domain_alias_address (username, ad_alias) AS
SELECT username, IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ' '), '') AS ad_alias FROM mailbox
LEFT OUTER JOIN alias_domain on target_domain=domain GROUP BY username;
CREATE TABLE IF NOT EXISTS sogo_acl (
c_folder_id INTEGER NOT NULL,
c_object character varying(255) NOT NULL,
c_uid character varying(255) NOT NULL,
c_role character varying(80) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS sogo_alarms_folder (
c_path VARCHAR(255) NOT NULL,
c_name VARCHAR(255) NOT NULL,
c_uid VARCHAR(255) NOT NULL,
c_recurrence_id INT(11) DEFAULT NULL,
c_alarm_number INT(11) NOT NULL,
c_alarm_date INT(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS sogo_cache_folder (
c_uid VARCHAR(255) NOT NULL,
c_path VARCHAR(255) NOT NULL,
c_parent_path VARCHAR(255) DEFAULT NULL,
c_type TINYINT(3) unsigned NOT NULL,
c_creationdate INT(11) NOT NULL,
c_lastmodified INT(11) NOT NULL,
c_version INT(11) NOT NULL DEFAULT '0',
c_deleted TINYINT(4) NOT NULL DEFAULT '0',
c_content longTEXT,
PRIMARY KEY (c_uid,c_path)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS sogo_folder_info (
c_folder_id BIGINT(20) unsigned NOT NULL AUTO_INCREMENT,
c_path VARCHAR(255) NOT NULL,
c_path1 VARCHAR(255) NOT NULL,
c_path2 VARCHAR(255) DEFAULT NULL,
c_path3 VARCHAR(255) DEFAULT NULL,
c_path4 VARCHAR(255) DEFAULT NULL,
c_foldername VARCHAR(255) NOT NULL,
c_location INTeger NULL,
c_quick_location VARCHAR(2048) DEFAULT NULL,
c_acl_location VARCHAR(2048) DEFAULT NULL,
c_folder_type VARCHAR(255) NOT NULL,
PRIMARY KEY (c_path),
UNIQUE KEY c_folder_id (c_folder_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS sogo_quick_appointment (
c_folder_id INTeger NOT NULL,
c_name character varying(255) NOT NULL,
c_uid character varying(255) NOT NULL,
c_startdate INTeger,
c_enddate INTeger,
c_cycleenddate INTeger,
c_title character varying(1000) NOT NULL,
c_participants TEXT,
c_isallday INTeger,
c_iscycle INTeger,
c_cycleinfo TEXT,
c_classification INTeger NOT NULL,
c_isopaque INTeger NOT NULL,
c_status INTeger NOT NULL,
c_priority INTeger,
c_location character varying(255),
c_orgmail character varying(255),
c_partmails TEXT,
c_partstates TEXT,
c_category character varying(255),
c_sequence INTeger,
c_component character varying(10) NOT NULL,
c_nextalarm INTeger,
c_description TEXT,
CONSTRAINT sogo_quick_appointment_pkey PRIMARY KEY (c_folder_id, c_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS sogo_quick_contact (
c_folder_id INTeger NOT NULL,
c_name character varying(255) NOT NULL,
c_givenname character varying(255),
c_cn character varying(255),
c_sn character varying(255),
c_screenname character varying(255),
c_l character varying(255),
c_mail character varying(255),
c_o character varying(255),
c_ou character varying(255),
c_telephonenumber character varying(255),
c_categories character varying(255),
c_component character varying(10) NOT NULL,
CONSTRAINT sogo_quick_contact_pkey PRIMARY KEY (c_folder_id, c_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS sogo_sessions_folder (
c_id VARCHAR(255) NOT NULL,
c_value VARCHAR(255) NOT NULL,
c_creationdate INT(11) NOT NULL,
c_lastseen INT(11) NOT NULL,
PRIMARY KEY (c_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS sogo_store (
c_folder_id INTeger NOT NULL,
c_name character varying(255) NOT NULL,
c_content mediumTEXT NOT NULL,
c_creationdate INTeger NOT NULL,
c_lastmodified INTeger NOT NULL,
c_version INTeger NOT NULL,
c_deleted INTeger,
CONSTRAINT sogo_store_pkey PRIMARY KEY (c_folder_id, c_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE IF NOT EXISTS sogo_user_profile (
c_uid VARCHAR(255) NOT NULL,
c_defaults TEXT,
c_settings TEXT,
PRIMARY KEY (c_uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
INSERT INTO `admin` (username, password, superadmin, created, modified, active) SELECT 'admin', '{SSHA256}K8eVJ6YsZbQCfuJvSUbaQRLr0HPLz5rC9IAp0PAFl0tmNDBkMDc0NDAyOTAxN2Rk', 1, NOW(), NOW(), 1 WHERE NOT EXISTS (SELECT * FROM `admin`);
DELETE FROM `domain_admins`;
INSERT INTO `domain_admins` (username, domain, created, active) SELECT `username`, 'ALL', NOW(), 1 FROM `admin` WHERE superadmin='1' AND `username` NOT IN (SELECT `username` FROM `domain_admins`);

View File

@ -0,0 +1,596 @@
<?php
function init_db_schema() {
try {
global $pdo;
$db_version = "23042017_1807";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
$stmt = $pdo->query("SELECT `version` FROM `versions`");
if ($stmt->fetch(PDO::FETCH_ASSOC)['version'] == $db_version) {
return true;
}
}
$views = array(
"grouped_mail_aliases" => "CREATE VIEW grouped_mail_aliases (username, aliases) AS
SELECT goto, IFNULL(GROUP_CONCAT(address SEPARATOR ' '), '') AS address FROM alias
WHERE address!=goto
AND active = '1'
AND address NOT LIKE '@%'
GROUP BY goto;",
"grouped_sender_acl" => "CREATE VIEW grouped_sender_acl (username, send_as) AS
SELECT logged_in_as, IFNULL(GROUP_CONCAT(send_as SEPARATOR ' '), '') AS send_as FROM sender_acl
WHERE send_as NOT LIKE '@%'
GROUP BY logged_in_as;",
"grouped_domain_alias_address" => "CREATE VIEW grouped_domain_alias_address (username, ad_alias) AS
SELECT username, IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ' '), '') AS ad_alias FROM mailbox
LEFT OUTER JOIN alias_domain on target_domain=domain GROUP BY username;"
);
$tables = array(
"versions" => array(
"cols" => array(
"application" => "VARCHAR(255) NOT NULL",
"version" => "VARCHAR(100) NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
),
"keys" => array(
"primary" => array(
"" => array("application")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"admin" => array(
"cols" => array(
"username" => "VARCHAR(255) NOT NULL",
"password" => "VARCHAR(255) NOT NULL",
"superadmin" => "TINYINT(1) NOT NULL DEFAULT '0'",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE NOW(0)",
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
),
"keys" => array(
"primary" => array(
"" => array("username")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"alias" => array(
"cols" => array(
"address" => "VARCHAR(255) NOT NULL",
"goto" => "TEXT NOT NULL",
"domain" => "VARCHAR(255) NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
),
"keys" => array(
"primary" => array(
"" => array("address")
),
"key" => array(
"domain" => array("domain")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sender_acl" => array(
"cols" => array(
"logged_in_as" => "VARCHAR(255) NOT NULL",
"send_as" => "VARCHAR(255) NOT NULL"
),
"keys" => array(),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"domain" => array(
"cols" => array(
"domain" => "VARCHAR(255) NOT NULL",
"description" => "VARCHAR(255)",
"aliases" => "INT(10) NOT NULL DEFAULT '0'",
"mailboxes" => "INT(10) NOT NULL DEFAULT '0'",
"maxquota" => "BIGINT(20) NOT NULL DEFAULT '0'",
"quota" => "BIGINT(20) NOT NULL DEFAULT '0'",
"transport" => "VARCHAR(255) NOT NULL",
"backupmx" => "TINYINT(1) NOT NULL DEFAULT '0'",
"relay_all_recipients" => "TINYINT(1) NOT NULL DEFAULT '0'",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
),
"keys" => array(
"primary" => array(
"" => array("domain")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"alias_domain" => array(
"cols" => array(
"alias_domain" => "VARCHAR(255) NOT NULL",
"target_domain" => "VARCHAR(255) NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
),
"keys" => array(
"primary" => array(
"" => array("alias_domain")
),
"key" => array(
"active" => array("active"),
"target_domain" => array("target_domain")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"spamalias" => array(
"cols" => array(
"address" => "VARCHAR(255) NOT NULL",
"goto" => "TEXT NOT NULL",
"validity" => "INT(11) NOT NULL"
),
"keys" => array(
"primary" => array(
"" => array("address")
),
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"filterconf" => array(
"cols" => array(
"object" => "VARCHAR(255) NOT NULL DEFAULT ''",
"option" => "VARCHAR(50) NOT NULL DEFAULT ''",
"value" => "VARCHAR(100) NOT NULL DEFAULT ''",
"prefid" => "INT(11) NOT NULL AUTO_INCREMENT"
),
"keys" => array(
"primary" => array(
"" => array("prefid")
),
"key" => array(
"object" => array("object")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"quota2" => array(
"cols" => array(
"username" => "VARCHAR(255) NOT NULL",
"bytes" => "BIGINT(20) NOT NULL DEFAULT '0'",
"messages" => "BIGINT(20) NOT NULL DEFAULT '0'"
),
"keys" => array(
"primary" => array(
"" => array("username")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"mailbox" => array(
"cols" => array(
"username" => "VARCHAR(255) NOT NULL",
"password" => "VARCHAR(255) NOT NULL",
"name" => "VARCHAR(255)",
"maildir" => "VARCHAR(255) NOT NULL",
"quota" => "BIGINT(20) NOT NULL DEFAULT '0'",
"local_part" => "VARCHAR(255) NOT NULL",
"domain" => "VARCHAR(255) NOT NULL",
"tls_enforce_in" => "TINYINT(1) NOT NULL DEFAULT '0'",
"tls_enforce_out" => "TINYINT(1) NOT NULL DEFAULT '0'",
"kind" => "VARCHAR(100) NOT NULL DEFAULT ''",
"multiple_bookings" => "TINYINT(1) NOT NULL DEFAULT '0'",
"wants_tagged_subject" => "TINYINT(1) NOT NULL DEFAULT '0'",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
),
"keys" => array(
"primary" => array(
"" => array("username")
),
"key" => array(
"domain" => array("domain")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"domain_admins" => array(
"cols" => array(
"username" => "VARCHAR(255) NOT NULL",
"domain" => "VARCHAR(255) NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
),
"keys" => array(
"key" => array(
"username" => array("username")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"imapsync" => array(
"cols" => array(
"id" => "INT NOT NULL AUTO_INCREMENT",
"user2" => "VARCHAR(255) NOT NULL",
"host1" => "VARCHAR(255) NOT NULL",
"authmech1" => "ENUM('PLAIN','LOGIN','CRAM-MD5') DEFAULT 'PLAIN'",
"regextrans2" => "VARCHAR(255) DEFAULT ''",
"authmd51" => "TINYINT(1) NOT NULL DEFAULT 0",
"domain2" => "VARCHAR(255) NOT NULL DEFAULT ''",
"subfolder2" => "VARCHAR(255) NOT NULL DEFAULT ''",
"user1" => "VARCHAR(255) NOT NULL",
"password1" => "VARCHAR(255) NOT NULL",
"exclude" => "VARCHAR(500) NOT NULL DEFAULT ''",
"maxage" => "SMALLINT NOT NULL DEFAULT '0'",
"mins_interval" => "VARCHAR(50) NOT NULL",
"port1" => "SMALLINT NOT NULL",
"enc1" => "ENUM('TLS','SSL','PLAIN') DEFAULT 'TLS'",
"delete2duplicates" => "TINYINT(1) NOT NULL DEFAULT '1'",
"returned_text" => "TEXT",
"last_run" => "TIMESTAMP NULL DEFAULT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
"active" => "TINYINT(1) NOT NULL DEFAULT '0'"
),
"keys" => array(
"primary" => array(
"" => array("id")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"tfa" => array(
"cols" => array(
"id" => "INT NOT NULL AUTO_INCREMENT",
"username" => "VARCHAR(255) NOT NULL",
"authmech" => "ENUM('yubi_otp', 'u2f', 'hotp', 'totp')",
"secret" => "VARCHAR(255) DEFAULT NULL",
"keyHandle" => "VARCHAR(255) DEFAULT NULL",
"publicKey" => "VARCHAR(255) DEFAULT NULL",
"counter" => "INT NOT NULL DEFAULT '0'",
"certificate" => "TEXT",
"active" => "TINYINT(1) NOT NULL DEFAULT '0'"
),
"keys" => array(
"primary" => array(
"" => array("id")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"forwarding_hosts" => array(
"cols" => array(
"host" => "VARCHAR(255) NOT NULL",
"source" => "VARCHAR(255) NOT NULL"
),
"keys" => array(
"primary" => array(
"" => array("host")
),
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sogo_acl" => array(
"cols" => array(
"c_folder_id" => "INT NOT NULL",
"c_object" => "VARCHAR(255) NOT NULL",
"c_uid" => "VARCHAR(255) NOT NULL",
"c_role" => "VARCHAR(80) NOT NULL"
),
"keys" => array(
"key" => array(
"sogo_acl_c_folder_id_idx" => array("c_folder_id"),
"sogo_acl_c_uid_idx" => array("c_uid")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sogo_alarms_folder" => array(
"cols" => array(
"c_path" => "VARCHAR(255) NOT NULL",
"c_name" => "VARCHAR(255) NOT NULL",
"c_uid" => "VARCHAR(255) NOT NULL",
"c_recurrence_id" => "INT(11) DEFAULT NULL",
"c_alarm_number" => "INT(11) NOT NULL",
"c_alarm_date" => "INT(11) NOT NULL"
),
"keys" => array(),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sogo_cache_folder" => array(
"cols" => array(
"c_uid" => "VARCHAR(255) NOT NULL",
"c_path" => "VARCHAR(255) NOT NULL",
"c_parent_path" => "VARCHAR(255) DEFAULT NULL",
"c_type" => "TINYINT(3) unsigned NOT NULL",
"c_creationdate" => "INT(11) NOT NULL",
"c_lastmodified" => "INT(11) NOT NULL",
"c_version" => "INT(11) NOT NULL DEFAULT '0'",
"c_deleted" => "TINYINT(4) NOT NULL DEFAULT '0'",
"c_content" => "LONGTEXT"
),
"keys" => array(
"primary" => array(
"" => array("c_uid", "c_path")
),
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sogo_folder_info" => array(
"cols" => array(
"c_folder_id" => "BIGINT(20) unsigned NOT NULL AUTO_INCREMENT",
"c_path" => "VARCHAR(255) NOT NULL",
"c_path1" => "VARCHAR(255) NOT NULL",
"c_path2" => "VARCHAR(255) DEFAULT NULL",
"c_path3" => "VARCHAR(255) DEFAULT NULL",
"c_path4" => "VARCHAR(255) DEFAULT NULL",
"c_foldername" => "VARCHAR(255) NOT NULL",
"c_location" => "INT NULL",
"c_quick_location" => "VARCHAR(2048) DEFAULT NULL",
"c_acl_location" => "VARCHAR(2048) DEFAULT NULL",
"c_folder_type" => "VARCHAR(255) NOT NULL"
),
"keys" => array(
"primary" => array(
"" => array("c_path")
),
"unique" => array(
"c_folder_id" => array("c_folder_id")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sogo_quick_appointment" => array(
"cols" => array(
"c_folder_id" => "INT NOT NULL",
"c_name" => "VARCHAR(255) NOT NULL",
"c_uid" => "VARCHAR(255) NOT NULL",
"c_startdate" => "INT",
"c_enddate" => "INT",
"c_cycleenddate" => "INT",
"c_title" => "VARCHAR(1000) NOT NULL",
"c_participants" => "TEXT",
"c_isallday" => "INT",
"c_iscycle" => "INT",
"c_cycleinfo" => "TEXT",
"c_classification" => "INT NOT NULL",
"c_isopaque" => "INT NOT NULL",
"c_status" => "INT NOT NULL",
"c_priority" => "INT",
"c_location" => "VARCHAR(255)",
"c_orgmail" => "VARCHAR(255)",
"c_partmails" => "TEXT",
"c_partstates" => "TEXT",
"c_category" => "VARCHAR(255)",
"c_sequence" => "INT",
"c_component" => "VARCHAR(10) NOT NULL",
"c_nextalarm" => "INT",
"c_description" => "TEXT"
),
"keys" => array(
"primary" => array(
"" => array("c_folder_id", "c_name")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sogo_quick_contact" => array(
"cols" => array(
"c_folder_id" => "INT NOT NULL",
"c_name" => "VARCHAR(255) NOT NULL",
"c_givenname" => "VARCHAR(255)",
"c_cn" => "VARCHAR(255)",
"c_sn" => "VARCHAR(255)",
"c_screenname" => "VARCHAR(255)",
"c_l" => "VARCHAR(255)",
"c_mail" => "VARCHAR(255)",
"c_o" => "VARCHAR(255)",
"c_ou" => "VARCHAR(255)",
"c_telephonenumber" => "VARCHAR(255)",
"c_categories" => "VARCHAR(255)",
"c_component" => "VARCHAR(10) NOT NULL"
),
"keys" => array(
"primary" => array(
"" => array("c_folder_id", "c_name")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sogo_sessions_folder" => array(
"cols" => array(
"c_id" => "VARCHAR(255) NOT NULL",
"c_value" => "VARCHAR(255) NOT NULL",
"c_creationdate" => "INT(11) NOT NULL",
"c_lastseen" => "INT(11) NOT NULL"
),
"keys" => array(
"primary" => array(
"" => array("c_id")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sogo_store" => array(
"cols" => array(
"c_folder_id" => "INT NOT NULL",
"c_name" => "VARCHAR(255) NOT NULL",
"c_content" => "MEDIUMTEXT NOT NULL",
"c_creationdate" => "INT NOT NULL",
"c_lastmodified" => "INT NOT NULL",
"c_version" => "INT NOT NULL",
"c_deleted" => "INT"
),
"keys" => array(
"primary" => array(
"" => array("c_folder_id", "c_name")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sogo_user_profile" => array(
"cols" => array(
"c_uid" => "VARCHAR(255) NOT NULL",
"c_defaults" => "TEXT",
"c_settings" => "TEXT"
),
"keys" => array(
"primary" => array(
"" => array("c_uid")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
)
);
foreach ($tables as $table => $properties) {
$stmt = $pdo->query("SHOW TABLES LIKE '" . $table . "'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
foreach($properties['cols'] as $column => $type) {
$stmt = $pdo->query("SHOW COLUMNS FROM `" . $table . "` LIKE '" . $column . "'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results == 0) {
$pdo->query("ALTER TABLE `" . $table . "` ADD `" . $column . "` " . $type);
}
else {
$pdo->query("ALTER TABLE `" . $table . "` MODIFY COLUMN `" . $column . "` " . $type);
}
}
foreach($properties['keys'] as $key_type => $key_content) {
if (strtolower($key_type) == 'primary') {
foreach ($key_content as $key_values) {
$fields = "`" . implode("`, `", $key_values) . "`";
$stmt = $pdo->query("SHOW KEYS FROM `" . $table . "` WHERE Key_name = 'PRIMARY'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$is_drop = ($num_results != 0) ? "DROP PRIMARY KEY, " : "";
$pdo->query("ALTER TABLE `" . $table . "` " . $is_drop . "ADD PRIMARY KEY (" . $fields . ")");
}
}
if (strtolower($key_type) == 'key') {
foreach ($key_content as $key_name => $key_values) {
$fields = "`" . implode("`, `", $key_values) . "`";
$stmt = $pdo->query("SHOW KEYS FROM `" . $table . "` WHERE Key_name = '" . $key_name . "'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$is_drop = ($num_results != 0) ? "DROP INDEX `" . $key_name . "`, " : "";
$pdo->query("ALTER TABLE `" . $table . "` " . $is_drop . "ADD KEY `" . $key_name . "` (" . $fields . ")");
}
}
if (strtolower($key_type) == 'unique') {
foreach ($key_content as $key_name => $key_values) {
$fields = "`" . implode("`, `", $key_values) . "`";
$stmt = $pdo->query("SHOW KEYS FROM `" . $table . "` WHERE Key_name = '" . $key_name . "'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$is_drop = ($num_results != 0) ? "DROP INDEX `" . $key_name . "`, " : "";
$pdo->query("ALTER TABLE `" . $table . "` " . $is_drop . "ADD UNIQUE KEY `" . $key_name . "` (" . $fields . ")");
}
}
}
// Drop all vanished columns
$stmt = $pdo->query("SHOW COLUMNS FROM `" . $table . "`");
$cols_in_table = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($cols_in_table)) {
if (!array_key_exists($row['Field'], $properties['cols'])) {
$pdo->query("ALTER TABLE `" . $table . "` DROP COLUMN `" . $row['Field'] . "`;");
}
}
// Step 1: Get all non-primary keys, that currently exist and those that should exist
$stmt = $pdo->query("SHOW KEYS FROM `" . $table . "` WHERE `Key_name` != 'PRIMARY'");
$keys_in_table = $stmt->fetchAll(PDO::FETCH_ASSOC);
$keys_to_exist = array();
if (isset($properties['keys']['unique']) && is_array($properties['keys']['unique'])) {
foreach ($properties['keys']['unique'] as $key_name => $key_values) {
$keys_to_exist[] = $key_name;
}
}
if (isset($properties['keys']['key']) && is_array($properties['keys']['key'])) {
foreach ($properties['keys']['key'] as $key_name => $key_values) {
$keys_to_exist[] = $key_name;
}
}
// Step 2: Drop all vanished indexes
while ($row = array_shift($keys_in_table)) {
if (!in_array($row['Key_name'], $keys_to_exist)) {
$pdo->query("ALTER TABLE `" . $table . "` DROP INDEX `" . $row['Key_name'] . "`");
}
}
// Step 3: Drop all vanished primary keys
if (!isset($properties['keys']['primary'])) {
$stmt = $pdo->query("SHOW KEYS FROM `" . $table . "` WHERE Key_name = 'PRIMARY'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
$pdo->query("ALTER TABLE `" . $table . "` DROP PRIMARY KEY");
}
}
}
else {
// Create table if it is missing
$sql = "CREATE TABLE IF NOT EXISTS `" . $table . "` (";
foreach($properties['cols'] as $column => $type) {
$sql .= $column . " " . $type . ",";
}
foreach($properties['keys'] as $key_type => $key_content) {
if (strtolower($key_type) == 'primary') {
foreach ($key_content as $key_values) {
$fields = "`" . implode("`, `", $key_values) . "`";
$sql .= "PRIMARY KEY (" . $fields . ")" . ",";
}
}
elseif (strtolower($key_type) == 'key') {
foreach ($key_content as $key_name => $key_values) {
$fields = "`" . implode("`, `", $key_values) . "`";
$sql .= "KEY `" . $key_name . "` (" . $fields . ")" . ",";
}
}
elseif (strtolower($key_type) == 'unique') {
foreach ($key_content as $key_name => $key_values) {
$fields = "`" . implode("`, `", $key_values) . "`";
$sql .= "UNIQUE KEY `" . $key_name . "` (" . $fields . ")" . ",";
}
}
}
$sql = rtrim($sql, ",");
$sql .= ") " . $properties['attr'];
$pdo->query($sql);
}
// Reset table attributes
$pdo->query("ALTER TABLE `" . $table . "` " . $properties['attr'] . ";");
}
// Recreate SQL views
foreach ($views as $view => $create) {
$pdo->query("DROP VIEW IF EXISTS `" . $view . "`;");
$pdo->query($create);
}
// Inject admin if not exists
$stmt = $pdo->query("INSERT INTO `admin` (`username`, `password`, `superadmin`, `created`, `modified`, `active`)
SELECT 'admin', '{SSHA256}K8eVJ6YsZbQCfuJvSUbaQRLr0HPLz5rC9IAp0PAFl0tmNDBkMDc0NDAyOTAxN2Rk', 1, NOW(), NOW(), 1
WHERE NOT EXISTS (SELECT * FROM `admin`);");
$stmt = $pdo->query("DELETE FROM `domain_admins`;");
$stmt = $pdo->query("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
SELECT `username`, 'ALL', NOW(), 1 FROM `admin`
WHERE superadmin='1' AND `username` NOT IN (SELECT `username` FROM `domain_admins`);");
// Insert new DB schema version
$stmt = $pdo->query("REPLACE INTO `versions` (`application`, `version`) VALUES ('db_schema', '" . $db_version . "');");
$_SESSION['return'] = array(
'type' => 'success',
'msg' => 'Database initialisation completed'
);
}
catch (PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Database initialisation failed: ' . $e->getMessage()
);
}
}
?>

View File

@ -29,18 +29,28 @@ require_once 'inc/lib/U2F.php';
$u2f = new u2flib_server\U2F('https://' . $_SERVER['SERVER_NAME']);
// PDO
$dsn = "$database_type:host=$database_host;dbname=$database_name";
// Calculate offset
$now = new DateTime();
$mins = $now->getOffset() / 60;
$sgn = ($mins < 0 ? -1 : 1);
$mins = abs($mins);
$hrs = floor($mins / 60);
$mins -= $hrs * 60;
$offset = sprintf('%+d:%02d', $hrs*$sgn, $mins);
$dsn = $database_type . ":host=" . $database_host . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = '" . $offset . "'",
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
?>
<center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>🐮 Connection failed, database may be in warm-up state, please try again later.<br /><br />The following error was reported:<br/> <?=$e->getMessage();?></center>
<center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>?? Connection failed, database may be in warm-up state, please try again later.<br /><br />The following error was reported:<br/> <?=$e->getMessage();?></center>
<?php
exit;
}
@ -106,5 +116,6 @@ if (isset($_GET['lang'])) {
require_once 'lang/lang.en.php';
include 'lang/lang.'.$_SESSION['mailcow_locale'].'.php';
require_once 'inc/functions.inc.php';
require_once 'inc/init_db.inc.php';
require_once 'inc/triggers.inc.php';
(!isset($_SESSION['mailcow_cc_username'])) ? init_db_schema() : null;
init_db_schema();

127
data/web/inc/spf.inc.php Normal file
View File

@ -0,0 +1,127 @@
<?php
function get_spf_allowed_hosts($domain)
{
$hosts = array();
$records = dns_get_record($domain, DNS_TXT);
foreach ($records as $record)
{
$txt = explode(' ', $record['entries'][0]);
if (array_shift($txt) != 'v=spf1') // only handle SPF records
continue;
foreach ($txt as $mech)
{
$qual = substr($mech, 0, 1);
if ($qual == '-' || $qual == '~') // only handle pass or neutral records
continue(2);
if ($qual == '+' || $qual == '?')
$mech = substr($mech, 1); // remove the qualifier
if (strpos($mech, '=') !== FALSE) // handle a modifier
{
$mod = explode('=', $mech);
if ($mod[0] == 'redirect') // handle a redirect
{
$hosts = get_spf_allowed_hosts($mod[1]);
return $hosts;
}
}
else
{
unset($cidr);
if (strpos($mech, ':') !== FALSE) // handle a domain specification
{
$split = explode(':', $mech);
$mech = array_shift($split);
$domain = implode(':', $split);
if (strpos($domain, '/') !== FALSE) // remove CIDR specification
{
$split = explode('/', $domain);
$domain = $split[0];
$cidr = $split[1];
}
}
$new_hosts = array();
if ($mech == 'include') // handle an inclusion
{
$new_hosts = get_spf_allowed_hosts($domain);
}
elseif ($mech == 'a') // handle a mechanism
{
$new_hosts = get_a_hosts($domain);
}
elseif ($mech == 'mx') // handle mx mechanism
{
$new_hosts = get_mx_hosts($domain);
}
elseif ($mech == 'ip4' || $mech == 'ip6') // handle ip mechanism
{
$new_hosts = array($domain);
}
if (isset($cidr)) // add CIDR specification if present
{
foreach ($new_hosts as &$host)
{
$host .= '/' . $cidr;
}
unset($host);
}
$hosts = array_unique(array_merge($hosts,$new_hosts), SORT_REGULAR);
}
}
}
return $hosts;
}
function get_mx_hosts($domain)
{
$hosts = array();
$mx_records = dns_get_record($domain, DNS_MX);
foreach ($mx_records as $mx_record)
{
$new_hosts = get_a_hosts($mx_record['target']);
$hosts = array_unique(array_merge($hosts,$new_hosts), SORT_REGULAR);
}
return $hosts;
}
function get_a_hosts($domain)
{
$hosts = array();
$a_records = dns_get_record($domain, DNS_A);
foreach ($a_records as $a_record)
{
$hosts[] = $a_record['ip'];
}
$a_records = dns_get_record($domain, DNS_AAAA);
foreach ($a_records as $a_record)
{
$hosts[] = $a_record['ipv6'];
}
return $hosts;
}
function get_outgoing_hosts_best_guess($domain)
{
// try the SPF record to get hosts that are allowed to send outgoing mails for this domain
$hosts = get_spf_allowed_hosts($domain);
if ($hosts) return $hosts;
// try the MX record to get mail servers for this domain
$hosts = get_mx_hosts($domain);
if ($hosts) return $hosts;
// fall back to the A record to get the host name for this domain
return get_a_hosts($domain);
}
?>

View File

@ -72,6 +72,12 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
if (isset($_POST["delete_domain_admin"])) {
delete_domain_admin($_POST);
}
if (isset($_POST["add_forwarding_host"])) {
add_forwarding_host($_POST);
}
if (isset($_POST["delete_forwarding_host"])) {
delete_forwarding_host($_POST);
}
}
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "user") {
if (isset($_POST["edit_user_account"])) {

View File

@ -450,11 +450,20 @@ $lang['admin']['unchanged_if_empty'] = 'Unverändert, wenn leer';
$lang['admin']['yes'] = '&#10004;';
$lang['admin']['no'] = '&#10008;';
$lang['admin']['access'] = 'Zugang';
$lang['admin']['invalid_max_msg_size'] = 'Invalid max. message size'; // NEEDS TRANSLATION
$lang['admin']['invalid_max_msg_size'] = 'Ungültige maximale Nachrichtengröße';
$lang['admin']['site_not_found'] = 'Kann mailcow Site-Konfiguration nicht finden';
$lang['admin']['public_folder_empty'] = 'Public folder name must not be empty'; // NEEDS TRANSLATION
$lang['admin']['public_folder_empty'] = 'Name des öffentlichen Ordners darf nicht leer sein';
$lang['admin']['set_rr_failed'] = 'Kann Postfix Restriktionen nicht setzen';
$lang['admin']['no_record'] = 'Kein Eintrag';
$lang['admin']['filter_table'] = 'Tabelle Filtern';
$lang['admin']['empty'] = 'Keine Einträge vorhanden';
$lang['admin']['forwarding_hosts'] = 'Weiterleitungs-Hosts';
$lang['admin']['forwarding_hosts_hint'] = 'Eingehende Nachrichten werden von den hier gelisteten Hosts bedingungslos akzeptiert. Diese Hosts werden dann nicht mit DNSBLs abgeglichen oder Greylisting unterworfen. Von ihnen empfangener Spam wird nie abgelehnt und immer in den Spam-Ordner einsortiert. Die übliche Verwendung für diese Funktion ist, um Mailserver anzugeben, auf denen eine Weiterleitung zu Ihrem Mailcow-Server eingerichtet wurde.';
$lang['admin']['forwarding_hosts_add_hint'] = 'Sie können entweder IPv4/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.';
$lang['edit']['host'] = 'Host';
$lang['edit']['source'] = 'Quelle';
$lang['admin']['add_forwarding_host'] = 'Weiterleitungs-Host hinzufügen';
$lang['delete']['remove_forwardinghost_warning'] = '<b>Warnung:</b> Sie entfernen den Weiterleitungs-Host <b>%s</b>!';
$lang['success']['forwarding_host_removed'] = "Weiterleitungs-Host %s wurde entfernt";
$lang['success']['forwarding_host_added'] = "Weiterleitungs-Host %s wurde hinzugefügt";
?>

View File

@ -468,4 +468,13 @@ $lang['admin']['set_rr_failed'] = 'Cannot set Postfix restrictions';
$lang['admin']['no_record'] = 'No record';
$lang['admin']['filter_table'] = 'Filter table';
$lang['admin']['empty'] = 'No results';
$lang['admin']['forwarding_hosts'] = 'Forwarding Hosts';
$lang['admin']['forwarding_hosts_hint'] = 'Incoming messages are unconditionally accepted from any hosts listed here. These hosts are then not checked against DNSBLs or subjected to greylisting. Spam received from them is never rejected and always filed into the Junk folder. The most common use for this is to specify mail servers on which you have set up a rule that forwards incoming emails to your Mailcow server.';
$lang['admin']['forwarding_hosts_add_hint'] = 'You can either specify IPv4/IPv6 addresses, networks in CIDR notation, host names (which will be resolved to IP addresses), or domain names (which will be resolved to IP addresses by querying SPF records or, in their absence, MX records).';
$lang['edit']['host'] = 'Host';
$lang['edit']['source'] = 'Source';
$lang['admin']['add_forwarding_host'] = 'Add Forwarding Host';
$lang['delete']['remove_forwardinghost_warning'] = '<b>Warning:</b> You are about to remove the forwarding host <b>%s</b>!';
$lang['success']['forwarding_host_removed'] = "Forwarding host %s has been removed";
$lang['success']['forwarding_host_added'] = "Forwarding host %s has been added";
?>

View File

@ -55,6 +55,17 @@ services:
aliases:
- redis
# clamd-mailcow:
# image: mailcow/clamd
# restart: always
# dns:
# - 172.22.1.254
# dns_search: mailcow-network
# networks:
# mailcow-network:
# aliases:
# - clamd
rspamd-mailcow:
image: mailcow/rspamd
build: ./data/Dockerfiles/rspamd

View File

@ -160,6 +160,84 @@ docker-compose exec postfix-mailcow chmod 660 /opt/postfix/conf/smarthost_passwd
docker-compose exec postfix-mailcow postfix reload
```
## Optional: Log to Syslog
Enable Rsyslog to receive logs on 524/tcp:
```
# This setting depends on your Rsyslog version and configuration format.
# For most Debian derivates it will work like this...
$ModLoad imtcp
$TCPServerAddress 127.0.0.1
$InputTCPServerRun 524
# ...while for Ubuntu 16.04 it looks like this:
module(load="imtcp")
input(type="imtcp" address="127.0.0.1" port="524")
# No matter your Rsyslog version, you should set this option to off
# if you plan to use Fail2ban
$RepeatedMsgReduction off
```
Restart rsyslog after enabling the TCP listener.
Now setup Docker daemon to start with the syslog driver.
This enables the syslog driver for all containers!
Debian users can change the startup configuration in `/etc/default/docker` while CentOS users find it in `/etc/sysconfig/docker`:
```
...
DOCKER_OPTS="--log-driver=syslog --log-opt syslog-address=tcp://127.0.0.1:524"
...
```
**Caution:** For some reason Ubuntu 16.04 and some, but not all, systemd based distros do not read the defaults file parameters.
Just run `systemctl edit docker.service` and add the following content to fix it.
**Note:** If "systemctl edit" is not available, just copy the content to `/etc/systemd/system/docker.service.d/override.conf`.
The first empty ExecStart parameter is not a mistake.
```
[Service]
EnvironmentFile=/etc/default/docker
ExecStart=
ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS
```
Restart the Docker daemon and run `docker-compose down && docker-compose up -d` to recreate the containers.
### Use Fail2ban
**This is a subsection of "Log to Syslog", which is required for Fail2ban to work.**
Open `/etc/fail2ban/filter.d/common.conf` and search for the prefix_line parameter, change it to ".*":
```
__prefix_line = .*
```
Create `/etc/fail2ban/jail.d/dovecot.conf`...
```
[dovecot]
enabled = true
filter = dovecot
logpath = /var/log/syslog
chain = FORWARD
```
and `jail.d/postfix-sasl.conf`:
```
[postfix-sasl]
enabled = true
filter = postfix-sasl
logpath = /var/log/syslog
chain = FORWARD
```
Restart Fail2ban.
## Install a local MTA

View File

@ -1,6 +1,8 @@
# mailcow: dockerized - 🐮 + 🐋 = 💕
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JWBSYHF4SMC68)
[![Servercow](https://www.servercow.de/img/cow_globe_200.svg)](https://www.servercow.de)
If you want to support mailcow, consider hosting mailcow on a Servercow virtual machine @ Servercow!
## Screenshots
@ -30,6 +32,7 @@ mailcow dockerized comes with **11 containers** linked in **one bridged network*
- mysql-vol-1
- rspamd-vol-1
- postfix-vol-1
- crypt-vol-1
The integrated **mailcow UI** allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access:

View File

@ -75,7 +75,7 @@ Beware that a mailbox user can login to mailcow and override a domain policy fil
Make your changes in `data/Dockerfiles/$service` and build the image locally:
```
docker build data/Dockerfiles/service -t andryyy/mailcow-dockerized:$service
docker build data/Dockerfiles/service -t mailcow/$service
```
Now auto-recreate modified containers:
@ -311,14 +311,11 @@ Running `docker-compose down -v` will **destroy all mailcow: dockerized volumes*
## Reset admin password
Reset mailcow admin to `admin:moohoo`:
1\. Drop admin table
```
source mailcow.conf
docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TABLE admin;"
cd mailcow_path
bash reset_admin.sh
```
2\. Open mailcow UI to auto-init the db
## Rspamd
### Learn spam and ham
@ -521,6 +518,14 @@ map $http_upgrade $connection_upgrade {
Now you can simply navigate to https://${MAILCOW_HOSTNAME}/portainer/ to view your Portainer container monitoring page. Youll then be prompted to specify a new password for the **admin** account. After specifying your password, youll then be able to connect to the Portainer UI.
## Change autodiscover setup type
This disables ActiveSync in the autodiscover service for Outlook and configures it with IMAP and SMTP instead:
Open `data/web/autodiscover.php` and set `'useEASforOutlook' => 'yes'` to `'useEASforOutlook' => 'no'`.
To always use IMAP and SMTP instead of EAS, set `'autodiscoverType' => 'imap'`.
## Why Bind?
For DNS blacklist lookups and DNSSEC.

36
mailcow-reset-admin.sh Executable file
View File

@ -0,0 +1,36 @@
#/bin/bash
if [[ ! -f mailcow.conf ]]; then
echo "Cannot find mailcow.conf, make sure this script is run from within the mailcow folder."
exit 1
fi
echo -n "Checking MySQL service... "
docker-compose ps -q mysql-mailcow > /dev/null 2>&1
if [[ $? -ne 0 ]]; then
echo "failed"
echo "MySQL (mysql-mailcow) is not up and running, exiting..."
exit 1
fi
echo "OK"
read -r -p "Are you sure you want to reset the mailcow administrator account? [y/N] " response
response=${response,,} # tolower
if [[ "$response" =~ ^(yes|y)$ ]]; then
echo -e "\nWorking, please wait..."
source mailcow.conf
docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM admin;"
docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "INSERT INTO admin (username, password, superadmin, created, modified, active) VALUES ('admin', '{SSHA256}K8eVJ6YsZbQCfuJvSUbaQRLr0HPLz5rC9IAp0PAFl0tmNDBkMDc0NDAyOTAxN2Rk', 1, NOW(), NOW(), 1);"
docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM domain_admins WHERE username='admin';"
docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "INSERT INTO domain_admins (username, domain, created, active) VALUES ('admin', 'ALL', NOW(), 1);"
docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM tfa WHERE username='admin';"
echo "
Reset credentials:
---
Username: admin
Password: moohoo
TFA: none
"
else
echo "Operation canceled."
fi

76
mailcow-setup-relayhost.sh Executable file
View File

@ -0,0 +1,76 @@
#/bin/bash
if [[ ! -f mailcow.conf ]]; then
echo "Cannot find mailcow.conf, make sure this script is run from within the mailcow folder."
exit 1
fi
echo -n "Checking Postfix service... "
docker-compose ps -q postfix-mailcow > /dev/null 2>&1
if [[ $? -ne 0 ]]; then
echo "failed"
echo "Postfix (postifx-mailcow) is not up and running, exiting..."
exit 1
fi
echo "OK"
if [[ -z ${1} ]]; then
echo "Usage:"
echo
echo "Setup a relayhost:"
echo "./${0} relayhost port (username) (password)"
echo "Username and password are optional parameters."
echo
echo "Reset to defaults:"
echo "./${0} reset"
exit 1
fi
if [[ ${1} == "reset" ]]; then
# Reset modified values to their defaults
sed -i "s/^relayhost\ \=.*/relayhost\ \=/" data/conf/postfix/main.cf
sed -i "s/^smtp\_sasl\_password\_maps.*/smtp\_sasl\_password\_maps\ \=/" data/conf/postfix/main.cf
sed -i "s/^smtp\_sasl\_security\_options.*/smtp\_sasl\_security\_options\ \=\ noplaintext\,\ noanonymous/" data/conf/postfix/main.cf
sed -i "s/^smtp\_sasl\_auth\_enable.*/smtp\_sasl\_auth\_enable\ \=\ no/" data/conf/postfix/main.cf
# Also delete the plaintext password file
rm -f data/conf/postfix/smarthost_passwd*
docker-compose exec postfix-mailcow postfix reload
# Exit with dc exit code
exit $?
else
# Try a simple connection to host:port but don't recieve any data
# Abort after 3 seconds
if ! nc -z -v -w3 ${1} ${2} 2>/dev/null; then
echo "Connection to relayhost ${1} failed, aborting..."
exit 1
fi
# Use exact hostname as relayhost, don't lookup the MX record of relayhost
sed -i "s/relayhost\ \=.*/relayhost\ \=\ \[${1}\]\:${2}/" data/conf/postfix/main.cf
if grep -q "smtp_sasl_password_maps" data/conf/postfix/main.cf
then
sed -i "s/^smtp\_sasl\_password\_maps.*/smtp\_sasl\_password\_maps\ \=\ hash\:\/opt\/postfix\/conf\/smarthost\_passwd/" data/conf/postfix/main.cf
else
echo "smtp_sasl_password_maps = hash:/opt/postfix/conf/smarthost_passwd" >> data/conf/postfix/main.cf
fi
if grep -q "smtp_sasl_auth_enable" data/conf/postfix/main.cf
then
sed -i "s/^smtp\_sasl\_auth\_enable.*/smtp\_sasl\_auth\_enable\ \=\ yes/" data/conf/postfix/main.cf
else
echo "smtp_sasl_auth_enable = yes" >> data/conf/postfix/main.cf
fi
if grep -q "smtp_sasl_security_options" data/conf/postfix/main.cf
then
sed -i "s/^smtp\_sasl\_security\_options.*/smtp\_sasl\_security\_options\ \=/" data/conf/postfix/main.cf
else
echo "smtp_sasl_security_options =" >> data/conf/postfix/main.cf
fi
if [[ ! -z ${3} ]]; then
echo ${1} ${3}:${4} > data/conf/postfix/smarthost_passwd
docker-compose exec postfix-mailcow postmap /opt/postfix/conf/smarthost_passwd
fi
docker-compose exec postfix-mailcow chown root:postfix /opt/postfix/conf/smarthost_passwd /opt/postfix/conf/smarthost_passwd.db
docker-compose exec postfix-mailcow chmod 660 /opt/postfix/conf/smarthost_passwd /opt/postfix/conf/smarthost_passwd.db
docker-compose exec postfix-mailcow postfix reload
exit $?
fi