blob: 7d8031aab79891cafb2151c5365e8def7097597f [file] [log] [blame]
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001<?php
2function relayhost($_action, $_data = null) {
3 global $pdo;
4 global $lang;
5 $_data_log = $_data;
6 switch ($_action) {
7 case 'add':
8 if ($_SESSION['mailcow_cc_role'] != "admin") {
9 $_SESSION['return'][] = array(
10 'type' => 'danger',
11 'log' => array(__FUNCTION__, $_action, $_data_log),
12 'msg' => 'access_denied'
13 );
14 return false;
15 }
16 $hostname = trim($_data['hostname']);
17 $username = str_replace(':', '\:', trim($_data['username']));
18 $password = str_replace(':', '\:', trim($_data['password']));
19 if (empty($hostname)) {
20 $_SESSION['return'][] = array(
21 'type' => 'danger',
22 'log' => array(__FUNCTION__, $_action, $_data_log),
23 'msg' => array('invalid_host', htmlspecialchars($host))
24 );
25 return false;
26 }
27 try {
28 $stmt = $pdo->prepare("INSERT INTO `relayhosts` (`hostname`, `username` ,`password`, `active`)
29 VALUES (:hostname, :username, :password, :active)");
30 $stmt->execute(array(
31 ':hostname' => $hostname,
32 ':username' => $username,
33 ':password' => str_replace(':', '\:', $password),
34 ':active' => '1'
35 ));
36 }
37 catch (PDOException $e) {
38 $_SESSION['return'][] = array(
39 'type' => 'danger',
40 'log' => array(__FUNCTION__, $_action, $_data_log),
41 'msg' => array('mysql_error', $e)
42 );
43 return false;
44 }
45 $_SESSION['return'][] = array(
46 'type' => 'success',
47 'log' => array(__FUNCTION__, $_action, $_data_log),
48 'msg' => array('relayhost_added', htmlspecialchars(implode(', ', $hosts)))
49 );
50 break;
51 case 'edit':
52 if ($_SESSION['mailcow_cc_role'] != "admin") {
53 $_SESSION['return'][] = array(
54 'type' => 'danger',
55 'log' => array(__FUNCTION__, $_action, $_data_log),
56 'msg' => 'access_denied'
57 );
58 return false;
59 }
60 $ids = (array)$_data['id'];
61 foreach ($ids as $id) {
62 $is_now = relayhost('details', $id);
63 if (!empty($is_now)) {
64 $hostname = (!empty($_data['hostname'])) ? trim($_data['hostname']) : $is_now['hostname'];
65 $username = (isset($_data['username'])) ? trim($_data['username']) : $is_now['username'];
66 $password = (isset($_data['password'])) ? trim($_data['password']) : $is_now['password'];
67 $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
68 }
69 else {
70 $_SESSION['return'][] = array(
71 'type' => 'danger',
72 'log' => array(__FUNCTION__, $_action, $_data_log),
73 'msg' => array('relayhost_invalid', $id)
74 );
75 continue;
76 }
77 try {
78 $stmt = $pdo->prepare("UPDATE `relayhosts` SET
79 `hostname` = :hostname,
80 `username` = :username,
81 `password` = :password,
82 `active` = :active
83 WHERE `id` = :id");
84 $stmt->execute(array(
85 ':id' => $id,
86 ':hostname' => $hostname,
87 ':username' => $username,
88 ':password' => $password,
89 ':active' => $active
90 ));
91 }
92 catch (PDOException $e) {
93 $_SESSION['return'][] = array(
94 'type' => 'danger',
95 'log' => array(__FUNCTION__, $_action, $_data_log),
96 'msg' => array('mysql_error', $e)
97 );
98 continue;
99 }
100 $_SESSION['return'][] = array(
101 'type' => 'success',
102 'log' => array(__FUNCTION__, $_action, $_data_log),
103 'msg' => array('object_modified', htmlspecialchars(implode(', ', $hostnames)))
104 );
105 }
106 break;
107 case 'delete':
108 if ($_SESSION['mailcow_cc_role'] != "admin") {
109 $_SESSION['return'][] = array(
110 'type' => 'danger',
111 'log' => array(__FUNCTION__, $_action, $_data_log),
112 'msg' => 'access_denied'
113 );
114 return false;
115 }
116 $ids = (array)$_data['id'];
117 foreach ($ids as $id) {
118 try {
119 $stmt = $pdo->prepare("DELETE FROM `relayhosts` WHERE `id`= :id");
120 $stmt->execute(array(':id' => $id));
121 $stmt = $pdo->prepare("UPDATE `domain` SET `relayhost` = '0' WHERE `relayhost`= :id");
122 $stmt->execute(array(':id' => $id));
123 }
124 catch (PDOException $e) {
125 $_SESSION['return'][] = array(
126 'type' => 'danger',
127 'log' => array(__FUNCTION__, $_action, $_data_log),
128 'msg' => array('mysql_error', $e)
129 );
130 continue;
131 }
132 $_SESSION['return'][] = array(
133 'type' => 'success',
134 'log' => array(__FUNCTION__, $_action, $_data_log),
135 'msg' => array('relayhost_removed', htmlspecialchars($id))
136 );
137 }
138 break;
139 case 'get':
140 if ($_SESSION['mailcow_cc_role'] != "admin") {
141 return false;
142 }
143 $relayhosts = array();
144 $stmt = $pdo->query("SELECT `id`, `hostname`, `username` FROM `relayhosts`");
145 $relayhosts = $stmt->fetchAll(PDO::FETCH_ASSOC);
146 return $relayhosts;
147 break;
148 case 'details':
149 if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
150 return false;
151 }
152 $relayhostdata = array();
153 $stmt = $pdo->prepare("SELECT `id`,
154 `hostname`,
155 `username`,
156 `password`,
157 `active`,
158 CONCAT(LEFT(`password`, 3), '...') AS `password_short`
159 FROM `relayhosts`
160 WHERE `id` = :id");
161 $stmt->execute(array(':id' => $_data));
162 $relayhostdata = $stmt->fetch(PDO::FETCH_ASSOC);
163 if (!empty($relayhostdata)) {
164 $stmt = $pdo->prepare("SELECT GROUP_CONCAT(`domain` SEPARATOR ', ') AS `used_by_domains` FROM `domain` WHERE `relayhost` = :id");
165 $stmt->execute(array(':id' => $_data));
166 $used_by_domains = $stmt->fetch(PDO::FETCH_ASSOC)['used_by_domains'];
167 $used_by_domains = (empty($used_by_domains)) ? '' : $used_by_domains;
168 $relayhostdata['used_by_domains'] = $used_by_domains;
169 }
170 return $relayhostdata;
171 break;
172 }
173}
174function transport($_action, $_data = null) {
175 global $pdo;
176 global $lang;
177 $_data_log = $_data;
178 switch ($_action) {
179 case 'add':
180 if ($_SESSION['mailcow_cc_role'] != "admin") {
181 $_SESSION['return'][] = array(
182 'type' => 'danger',
183 'log' => array(__FUNCTION__, $_action, $_data_log),
184 'msg' => 'access_denied'
185 );
186 return false;
187 }
188 $destinations = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['destination']));
189 $active = intval($_data['active']);
190 $lookup_mx = intval($_data['lookup_mx']);
191 $nexthop = trim($_data['nexthop']);
192 if (filter_var($nexthop, FILTER_VALIDATE_IP)) {
193 $nexthop = '[' . $nexthop . ']';
194 }
195 preg_match('/\[(.+)\].*/', $nexthop, $next_hop_matches);
196 $next_hop_clean = (isset($next_hop_matches[1])) ? $next_hop_matches[1] : $nexthop;
197 $username = str_replace(':', '\:', trim($_data['username']));
198 $password = str_replace(':', '\:', trim($_data['password']));
199 if (empty($nexthop)) {
200 $_SESSION['return'][] = array(
201 'type' => 'danger',
202 'log' => array(__FUNCTION__, $_action, $_data_log),
203 'msg' => array('invalid_nexthop')
204 );
205 return false;
206 }
207 $transports = transport('get');
208 if (!empty($transports)) {
209 foreach ($transports as $transport) {
210 $transport_data = transport('details', $transport['id']);
211 $existing_nh[] = $transport_data['nexthop'];
212 preg_match('/\[(.+)\].*/', $transport_data['nexthop'], $existing_clean_nh[]);
213 if (($transport_data['nexthop'] == $nexthop || $transport_data['nexthop'] == $next_hop_clean) && $transport_data['username'] != $username) {
214 $_SESSION['return'][] = array(
215 'type' => 'danger',
216 'log' => array(__FUNCTION__, $_action, $_data_log),
217 'msg' => 'invalid_nexthop_authenticated'
218 );
219 return false;
220 }
221 foreach ($destinations as $d_ix => &$dest) {
222 if (empty($dest)) {
223 unset($destinations[$d_ix]);
224 continue;
225 }
226 if ($transport_data['destination'] == $dest) {
227 $_SESSION['return'][] = array(
228 'type' => 'danger',
229 'log' => array(__FUNCTION__, $_action, $_data_log),
230 'msg' => array('transport_dest_exists', $dest)
231 );
232 unset($destinations[$d_ix]);
233 continue;
234 }
235 // ".domain" is a valid destination, "..domain" is not
236 if (empty($dest) || (is_valid_domain_name(preg_replace('/^' . preg_quote('.', '/') . '/', '', $dest)) === false && $dest != '*' && filter_var($dest, FILTER_VALIDATE_EMAIL) === false)) {
237 $_SESSION['return'][] = array(
238 'type' => 'danger',
239 'log' => array(__FUNCTION__, $_action, $_data_log),
240 'msg' => array('invalid_destination', $dest)
241 );
242 unset($destinations[$d_ix]);
243 continue;
244 }
245 }
246 }
247 }
248 $destinations = array_filter(array_values(array_unique($destinations)));
249 if (empty($destinations)) { return false; }
250 if (isset($next_hop_matches[1])) {
251 if (in_array($next_hop_clean, $existing_nh)) {
252 $_SESSION['return'][] = array(
253 'type' => 'danger',
254 'log' => array(__FUNCTION__, $_action, $_data_log),
255 'msg' => array('next_hop_interferes', $next_hop_clean, $nexthop)
256 );
257 return false;
258 }
259 }
260 else {
261 foreach ($existing_clean_nh as $existing_clean_nh_each) {
262 if ($existing_clean_nh_each[1] == $nexthop) {
263 $_SESSION['return'][] = array(
264 'type' => 'danger',
265 'log' => array(__FUNCTION__, $_action, $_data_log),
266 'msg' => array('next_hop_interferes_any', $nexthop)
267 );
268 return false;
269 }
270 }
271 }
272 foreach ($destinations as $insert_dest) {
273 $stmt = $pdo->prepare("INSERT INTO `transports` (`nexthop`, `destination`, `username` , `password`, `lookup_mx`, `active`)
274 VALUES (:nexthop, :destination, :username, :password, :lookup_mx, :active)");
275 $stmt->execute(array(
276 ':nexthop' => $nexthop,
277 ':destination' => $insert_dest,
278 ':username' => $username,
279 ':password' => str_replace(':', '\:', $password),
280 ':lookup_mx' => $lookup_mx,
281 ':active' => $active
282 ));
283 }
284 $stmt = $pdo->prepare("UPDATE `transports` SET
285 `username` = :username,
286 `password` = :password
287 WHERE `nexthop` = :nexthop");
288 $stmt->execute(array(
289 ':nexthop' => $nexthop,
290 ':username' => $username,
291 ':password' => $password
292 ));
293 $_SESSION['return'][] = array(
294 'type' => 'success',
295 'log' => array(__FUNCTION__, $_action, $_data_log),
296 'msg' => array('relayhost_added', htmlspecialchars(implode(', ', $hosts)))
297 );
298 break;
299 case 'edit':
300 if ($_SESSION['mailcow_cc_role'] != "admin") {
301 $_SESSION['return'][] = array(
302 'type' => 'danger',
303 'log' => array(__FUNCTION__, $_action, $_data_log),
304 'msg' => 'access_denied'
305 );
306 return false;
307 }
308 $ids = (array)$_data['id'];
309 foreach ($ids as $id) {
310 $is_now = transport('details', $id);
311 if (!empty($is_now)) {
312 $destination = (!empty($_data['destination'])) ? trim($_data['destination']) : $is_now['destination'];
313 $nexthop = (!empty($_data['nexthop'])) ? trim($_data['nexthop']) : $is_now['nexthop'];
314 $username = (isset($_data['username'])) ? trim($_data['username']) : $is_now['username'];
315 $password = (isset($_data['password'])) ? trim($_data['password']) : $is_now['password'];
316 $lookup_mx = (isset($_data['lookup_mx']) && $_data['lookup_mx'] != '') ? intval($_data['lookup_mx']) : $is_now['lookup_mx'];
317 $active = (isset($_data['active']) && $_data['active'] != '') ? intval($_data['active']) : $is_now['active'];
318 }
319 else {
320 $_SESSION['return'][] = array(
321 'type' => 'danger',
322 'log' => array(__FUNCTION__, $_action, $_data_log),
323 'msg' => array('relayhost_invalid', $id)
324 );
325 continue;
326 }
327 preg_match('/\[(.+)\].*/', $nexthop, $next_hop_matches);
328 if (filter_var($nexthop, FILTER_VALIDATE_IP)) {
329 $nexthop = '[' . $nexthop . ']';
330 }
331 $next_hop_clean = (isset($next_hop_matches[1])) ? $next_hop_matches[1] : $nexthop;
332 $transports = transport('get');
333 if (!empty($transports)) {
334 foreach ($transports as $transport) {
335 $transport_data = transport('details', $transport['id']);
336 if ($transport['id'] == $id) {
337 continue;
338 }
339 $existing_nh[] = $transport_data['nexthop'];
340 preg_match('/\[(.+)\].*/', $transport_data['nexthop'], $existing_clean_nh[]);
341 if ($transport_data['destination'] == $destination) {
342 $_SESSION['return'][] = array(
343 'type' => 'danger',
344 'log' => array(__FUNCTION__, $_action, $_data_log),
345 'msg' => 'transport_dest_exists'
346 );
347 return false;
348 }
349 }
350 }
351 if (isset($next_hop_matches[1])) {
352 if (in_array($next_hop_clean, $existing_nh)) {
353 $_SESSION['return'][] = array(
354 'type' => 'danger',
355 'log' => array(__FUNCTION__, $_action, $_data_log),
356 'msg' => array('next_hop_interferes', $next_hop_clean, $nexthop)
357 );
358 return false;
359 }
360 }
361 else {
362 foreach ($existing_clean_nh as $existing_clean_nh_each) {
363 if ($existing_clean_nh_each[1] == $nexthop) {
364 $_SESSION['return'][] = array(
365 'type' => 'danger',
366 'log' => array(__FUNCTION__, $_action, $_data_log),
367 'msg' => array('next_hop_interferes_any', $nexthop)
368 );
369 return false;
370 }
371 }
372 }
373 if (empty($username)) {
374 $password = '';
375 }
376 try {
377 $stmt = $pdo->prepare("UPDATE `transports` SET
378 `destination` = :destination,
379 `nexthop` = :nexthop,
380 `username` = :username,
381 `password` = :password,
382 `lookup_mx` = :lookup_mx,
383 `active` = :active
384 WHERE `id` = :id");
385 $stmt->execute(array(
386 ':id' => $id,
387 ':destination' => $destination,
388 ':nexthop' => $nexthop,
389 ':username' => $username,
390 ':password' => $password,
391 ':lookup_mx' => $lookup_mx,
392 ':active' => $active
393 ));
394 $stmt = $pdo->prepare("UPDATE `transports` SET
395 `username` = :username,
396 `password` = :password
397 WHERE `nexthop` = :nexthop");
398 $stmt->execute(array(
399 ':nexthop' => $nexthop,
400 ':username' => $username,
401 ':password' => $password
402 ));
403 }
404 catch (PDOException $e) {
405 $_SESSION['return'][] = array(
406 'type' => 'danger',
407 'log' => array(__FUNCTION__, $_action, $_data_log),
408 'msg' => array('mysql_error', $e)
409 );
410 continue;
411 }
412 $_SESSION['return'][] = array(
413 'type' => 'success',
414 'log' => array(__FUNCTION__, $_action, $_data_log),
415 'msg' => array('object_modified', htmlspecialchars(implode(', ', $hostnames)))
416 );
417 }
418 break;
419 case 'delete':
420 if ($_SESSION['mailcow_cc_role'] != "admin") {
421 $_SESSION['return'][] = array(
422 'type' => 'danger',
423 'log' => array(__FUNCTION__, $_action, $_data_log),
424 'msg' => 'access_denied'
425 );
426 return false;
427 }
428 $ids = (array)$_data['id'];
429 foreach ($ids as $id) {
430 try {
431 $stmt = $pdo->prepare("DELETE FROM `transports` WHERE `id`= :id");
432 $stmt->execute(array(':id' => $id));
433 }
434 catch (PDOException $e) {
435 $_SESSION['return'][] = array(
436 'type' => 'danger',
437 'log' => array(__FUNCTION__, $_action, $_data_log),
438 'msg' => array('mysql_error', $e)
439 );
440 continue;
441 }
442 $_SESSION['return'][] = array(
443 'type' => 'success',
444 'log' => array(__FUNCTION__, $_action, $_data_log),
445 'msg' => array('relayhost_removed', htmlspecialchars($id))
446 );
447 }
448 break;
449 case 'get':
450 if ($_SESSION['mailcow_cc_role'] != "admin") {
451 return false;
452 }
453 $transports = array();
454 $stmt = $pdo->query("SELECT `id`, `destination`, `nexthop`, `username` FROM `transports`");
455 $transports = $stmt->fetchAll(PDO::FETCH_ASSOC);
456 return $transports;
457 break;
458 case 'details':
459 if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
460 return false;
461 }
462 $transportdata = array();
463 $stmt = $pdo->prepare("SELECT `id`,
464 `destination`,
465 `nexthop`,
466 `username`,
467 `password`,
468 `active`,
469 `lookup_mx`,
470 CONCAT(LEFT(`password`, 3), '...') AS `password_short`
471 FROM `transports`
472 WHERE `id` = :id");
473 $stmt->execute(array(':id' => $_data));
474 $transportdata = $stmt->fetch(PDO::FETCH_ASSOC);
475 return $transportdata;
476 break;
477 }
478}