settings.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. <?php
  2. /*
  3. The match section performs AND operation on different matches: for example, if you have from and rcpt in the same rule,
  4. then the rule matches only when from AND rcpt match. For similar matches, the OR rule applies: if you have multiple rcpt matches,
  5. then any of these will trigger the rule. If a rule is triggered then no more rules are matched.
  6. */
  7. header('Content-Type: text/plain');
  8. require_once "vars.inc.php";
  9. // Getting headers sent by the client.
  10. //$headers = apache_request_headers();
  11. ini_set('error_reporting', 0);
  12. //$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
  13. $dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
  14. $opt = [
  15. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  16. PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  17. PDO::ATTR_EMULATE_PREPARES => false,
  18. ];
  19. try {
  20. $pdo = new PDO($dsn, $database_user, $database_pass, $opt);
  21. $stmt = $pdo->query("SELECT '1' FROM `filterconf`");
  22. }
  23. catch (PDOException $e) {
  24. echo 'settings { }';
  25. exit;
  26. }
  27. // Check if db changed and return header
  28. /*$stmt = $pdo->prepare("SELECT UNIX_TIMESTAMP(UPDATE_TIME) AS `db_update_time` FROM information_schema.tables
  29. WHERE `TABLE_NAME` = 'filterconf'
  30. AND TABLE_SCHEMA = :dbname;");
  31. $stmt->execute(array(
  32. ':dbname' => $database_name
  33. ));
  34. $db_update_time = $stmt->fetch(PDO::FETCH_ASSOC)['db_update_time'];
  35. if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == $db_update_time)) {
  36. header('Last-Modified: '.gmdate('D, d M Y H:i:s', $db_update_time).' GMT', true, 304);
  37. exit;
  38. } else {
  39. header('Last-Modified: '.gmdate('D, d M Y H:i:s', $db_update_time).' GMT', true, 200);
  40. }
  41. */
  42. function parse_email($email) {
  43. if (!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
  44. $a = strrpos($email, '@');
  45. return array('local' => substr($email, 0, $a), 'domain' => substr($email, $a));
  46. }
  47. function wl_by_sogo() {
  48. global $pdo;
  49. $rcpt = array();
  50. $stmt = $pdo->query("SELECT DISTINCT(`sogo_folder_info`.`c_path2`) AS `user`, GROUP_CONCAT(`sogo_quick_contact`.`c_mail`) AS `contacts` FROM `sogo_folder_info`
  51. INNER JOIN `sogo_quick_contact` ON `sogo_quick_contact`.`c_folder_id` = `sogo_folder_info`.`c_folder_id`
  52. GROUP BY `c_path2`");
  53. $sogo_contacts = $stmt->fetchAll(PDO::FETCH_ASSOC);
  54. while ($row = array_shift($sogo_contacts)) {
  55. foreach (explode(',', $row['contacts']) as $contact) {
  56. if (!filter_var($contact, FILTER_VALIDATE_EMAIL)) {
  57. continue;
  58. }
  59. $rcpt[$row['user']][] = '/^' . str_replace('/', '\/', $contact) . '$/i';
  60. }
  61. }
  62. return $rcpt;
  63. }
  64. function ucl_rcpts($object, $type) {
  65. global $pdo;
  66. $rcpt = array();
  67. if ($type == 'mailbox') {
  68. // Standard aliases
  69. $stmt = $pdo->prepare("SELECT `address` FROM `alias`
  70. WHERE `goto` = :object_goto
  71. AND `address` NOT LIKE '@%'");
  72. $stmt->execute(array(
  73. ':object_goto' => $object
  74. ));
  75. $standard_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
  76. while ($row = array_shift($standard_aliases)) {
  77. $local = parse_email($row['address'])['local'];
  78. $domain = parse_email($row['address'])['domain'];
  79. if (!empty($local) && !empty($domain)) {
  80. $rcpt[] = '/^' . str_replace('/', '\/', $local) . '[+].*' . str_replace('/', '\/', $domain) . '$/i';
  81. }
  82. $rcpt[] = '/^' . str_replace('/', '\/', $row['address']) . '$/i';
  83. }
  84. // Aliases by alias domains
  85. $stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `alias` FROM `mailbox`
  86. LEFT OUTER JOIN `alias_domain` ON `mailbox`.`domain` = `alias_domain`.`target_domain`
  87. WHERE `mailbox`.`username` = :object");
  88. $stmt->execute(array(
  89. ':object' => $object
  90. ));
  91. $by_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
  92. array_filter($by_domain_aliases);
  93. while ($row = array_shift($by_domain_aliases)) {
  94. if (!empty($row['alias'])) {
  95. $local = parse_email($row['alias'])['local'];
  96. $domain = parse_email($row['alias'])['domain'];
  97. if (!empty($local) && !empty($domain)) {
  98. $rcpt[] = '/^' . str_replace('/', '\/', $local) . '[+].*' . str_replace('/', '\/', $domain) . '$/i';
  99. }
  100. $rcpt[] = '/^' . str_replace('/', '\/', $row['alias']) . '$/i';
  101. }
  102. }
  103. }
  104. elseif ($type == 'domain') {
  105. // Domain self
  106. $rcpt[] = '/.*@' . $object . '/i';
  107. $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
  108. WHERE `target_domain` = :object");
  109. $stmt->execute(array(':object' => $object));
  110. $alias_domains = $stmt->fetchAll(PDO::FETCH_ASSOC);
  111. array_filter($alias_domains);
  112. while ($row = array_shift($alias_domains)) {
  113. $rcpt[] = '/.*@' . $row['alias_domain'] . '/i';
  114. }
  115. }
  116. return $rcpt;
  117. }
  118. ?>
  119. settings {
  120. watchdog {
  121. priority = 10;
  122. rcpt_mime = "/null@localhost/i";
  123. from_mime = "/watchdog@localhost/i";
  124. apply "default" {
  125. actions {
  126. reject = 9999.0;
  127. greylist = 9998.0;
  128. "add header" = 9997.0;
  129. }
  130. }
  131. }
  132. <?php
  133. /*
  134. // Start custom scores for users
  135. */
  136. $stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'highspamlevel' OR `option` = 'lowspamlevel'");
  137. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
  138. while ($row = array_shift($rows)) {
  139. $username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
  140. ?>
  141. score_<?=$username_sane;?> {
  142. priority = 4;
  143. <?php
  144. foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
  145. ?>
  146. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  147. <?php
  148. }
  149. $stmt = $pdo->prepare("SELECT `option`, `value` FROM `filterconf`
  150. WHERE (`option` = 'highspamlevel' OR `option` = 'lowspamlevel')
  151. AND `object`= :object");
  152. $stmt->execute(array(':object' => $row['object']));
  153. $spamscore = $stmt->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP);
  154. ?>
  155. apply "default" {
  156. actions {
  157. reject = <?=$spamscore['highspamlevel'][0];?>;
  158. greylist = <?=$spamscore['lowspamlevel'][0] - 1;?>;
  159. "add header" = <?=$spamscore['lowspamlevel'][0];?>;
  160. }
  161. }
  162. }
  163. <?php
  164. }
  165. /*
  166. // Start SOGo contacts whitelist
  167. // Priority 4, lower than a domain whitelist (5) and lower than a mailbox whitelist (6)
  168. */
  169. foreach (wl_by_sogo() as $user => $contacts) {
  170. $username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $user);
  171. ?>
  172. whitelist_sogo_<?=$username_sane;?> {
  173. <?php
  174. foreach ($contacts as $contact) {
  175. ?>
  176. from = <?=json_encode($contact, JSON_UNESCAPED_SLASHES);?>;
  177. <?php
  178. }
  179. ?>
  180. priority = 4;
  181. <?php
  182. foreach (ucl_rcpts($user, 'mailbox') as $rcpt) {
  183. ?>
  184. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  185. <?php
  186. }
  187. ?>
  188. apply "default" {
  189. SOGO_CONTACT = -99.0;
  190. }
  191. symbols [
  192. "SOGO_CONTACT"
  193. ]
  194. }
  195. <?php
  196. }
  197. /*
  198. // Start whitelist
  199. */
  200. $stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'whitelist_from'");
  201. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
  202. while ($row = array_shift($rows)) {
  203. $username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
  204. ?>
  205. whitelist_<?=$username_sane;?> {
  206. <?php
  207. $list_items = array();
  208. $stmt = $pdo->prepare("SELECT `value` FROM `filterconf`
  209. WHERE `object`= :object
  210. AND `option` = 'whitelist_from'");
  211. $stmt->execute(array(':object' => $row['object']));
  212. $list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
  213. foreach ($list_items as $item) {
  214. ?>
  215. from = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
  216. <?php
  217. }
  218. if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
  219. ?>
  220. priority = 5;
  221. <?php
  222. foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
  223. ?>
  224. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  225. <?php
  226. }
  227. }
  228. else {
  229. ?>
  230. priority = 6;
  231. <?php
  232. foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
  233. ?>
  234. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  235. <?php
  236. }
  237. }
  238. ?>
  239. apply "default" {
  240. MAILCOW_WHITE = -999.0;
  241. }
  242. symbols [
  243. "MAILCOW_WHITE"
  244. ]
  245. }
  246. whitelist_mime_<?=$username_sane;?> {
  247. <?php
  248. foreach ($list_items as $item) {
  249. ?>
  250. from_mime = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
  251. <?php
  252. }
  253. if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
  254. ?>
  255. priority = 5;
  256. <?php
  257. foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
  258. ?>
  259. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  260. <?php
  261. }
  262. }
  263. else {
  264. ?>
  265. priority = 6;
  266. <?php
  267. foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
  268. ?>
  269. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  270. <?php
  271. }
  272. }
  273. ?>
  274. apply "default" {
  275. MAILCOW_WHITE = -999.0;
  276. }
  277. symbols [
  278. "MAILCOW_WHITE"
  279. ]
  280. }
  281. <?php
  282. }
  283. /*
  284. // Start blacklist
  285. */
  286. $stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'blacklist_from'");
  287. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
  288. while ($row = array_shift($rows)) {
  289. $username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
  290. ?>
  291. blacklist_<?=$username_sane;?> {
  292. <?php
  293. $list_items = array();
  294. $stmt = $pdo->prepare("SELECT `value` FROM `filterconf`
  295. WHERE `object`= :object
  296. AND `option` = 'blacklist_from'");
  297. $stmt->execute(array(':object' => $row['object']));
  298. $list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
  299. foreach ($list_items as $item) {
  300. ?>
  301. from = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
  302. <?php
  303. }
  304. if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
  305. ?>
  306. priority = 5;
  307. <?php
  308. foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
  309. ?>
  310. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  311. <?php
  312. }
  313. }
  314. else {
  315. ?>
  316. priority = 6;
  317. <?php
  318. foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
  319. ?>
  320. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  321. <?php
  322. }
  323. }
  324. ?>
  325. apply "default" {
  326. MAILCOW_BLACK = 999.0;
  327. }
  328. symbols [
  329. "MAILCOW_BLACK"
  330. ]
  331. }
  332. blacklist_header_<?=$username_sane;?> {
  333. <?php
  334. foreach ($list_items as $item) {
  335. ?>
  336. from_mime = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
  337. <?php
  338. }
  339. if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
  340. ?>
  341. priority = 5;
  342. <?php
  343. foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
  344. ?>
  345. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  346. <?php
  347. }
  348. }
  349. else {
  350. ?>
  351. priority = 6;
  352. <?php
  353. foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
  354. ?>
  355. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  356. <?php
  357. }
  358. }
  359. ?>
  360. apply "default" {
  361. MAILCOW_BLACK = 999.0;
  362. }
  363. symbols [
  364. "MAILCOW_BLACK"
  365. ]
  366. }
  367. <?php
  368. }
  369. /*
  370. // Start traps
  371. */
  372. ?>
  373. traps {
  374. <?php
  375. foreach (ucl_rcpts('spam@localhost', 'mailbox') as $rcpt) {
  376. ?>
  377. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  378. <?php
  379. }
  380. foreach (ucl_rcpts('ham@localhost', 'mailbox') as $rcpt) {
  381. ?>
  382. rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
  383. <?php
  384. }
  385. ?>
  386. priority = 9;
  387. want_spam = yes;
  388. }
  389. <?php
  390. // Start additional content
  391. $stmt = $pdo->query("SELECT `id`, `content` FROM `settingsmap` WHERE `active` = '1'");
  392. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
  393. while ($row = array_shift($rows)) {
  394. $username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['id']);
  395. ?>
  396. additional_settings_<?=intval($row['id']);?> {
  397. <?php
  398. $content = preg_split('/\r\n|\r|\n/', $row['content']);
  399. foreach ($content as $line) {
  400. echo ' ' . $line . PHP_EOL;
  401. }
  402. ?>
  403. }
  404. <?php
  405. }
  406. ?>
  407. }