blob: 595bd8f902f062adfccc5c3a022d452b4239fbec [file] [log] [blame]
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001<?php
2/*
3 see /api
4*/
5
6header('Content-Type: application/json');
7require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
8error_reporting(0);
9
10function api_log($_data) {
11 global $redis;
12 $data_var = array();
13 foreach ($_data as $data => &$value) {
14 if ($data == 'csrf_token') {
15 continue;
16 }
17 if ($value = json_decode($value, true)) {
18 unset($value["csrf_token"]);
19 foreach ($value as $key => &$val) {
20 if(preg_match("/pass/i", $key)) {
21 $val = '*';
22 }
23 }
24 $value = json_encode($value);
25 }
26 $data_var[] = $data . "='" . $value . "'";
27 }
28 try {
29 $log_line = array(
30 'time' => time(),
31 'uri' => $_SERVER['REQUEST_URI'],
32 'method' => $_SERVER['REQUEST_METHOD'],
33 'remote' => get_remote_ip(),
34 'data' => implode(', ', $data_var)
35 );
36 $redis->lPush('API_LOG', json_encode($log_line));
37 }
38 catch (RedisException $e) {
39 $_SESSION['return'][] = array(
40 'type' => 'danger',
41 'msg' => 'Redis: '.$e
42 );
43 return false;
44 }
45}
46
47if (isset($_GET['query'])) {
48
49 $query = explode('/', $_GET['query']);
50 $action = (isset($query[0])) ? $query[0] : null;
51 $category = (isset($query[1])) ? $query[1] : null;
52 $object = (isset($query[2])) ? $query[2] : null;
53 $extra = (isset($query[3])) ? $query[3] : null;
54
55 // accept json in request body
56 if(strpos($_SERVER['HTTP_CONTENT_TYPE'], 'application/json') !== false) {
57 $request = file_get_contents('php://input');
58 $requestDecoded = json_decode($request, true);
59
60 // check for valid json
61 if ($action != 'get' && $requestDecoded === null) {
62 http_response_code(400);
63 echo json_encode(array(
64 'type' => 'error',
65 'msg' => 'Request body doesn\'t contain valid json!'
66 ));
67 exit;
68 }
69
70 // add
71 if ($action == 'add') {
72 $_POST['attr'] = $request;
73 }
74
75 // edit
76 if ($action == 'edit') {
77 $_POST['attr'] = json_encode($requestDecoded['attr']);
78 $_POST['items'] = json_encode($requestDecoded['items']);
79 }
80
81 // delete
82 if ($action == 'delete') {
83 $_POST['items'] = $request;
84 }
85
86 }
87 api_log($_POST);
88
89 $request_incomplete = json_encode(array(
90 'type' => 'error',
91 'msg' => 'Cannot find attributes in post data'
92 ));
93
94 switch ($action) {
95 case "add":
96 if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username'])) {
97 http_response_code(403);
98 echo json_encode(array(
99 'type' => 'error',
100 'msg' => 'API read/write access denied'
101 ));
102 exit();
103 }
104 function process_add_return($return) {
105 $generic_failure = json_encode(array(
106 'type' => 'error',
107 'msg' => 'Cannot add item'
108 ));
109 $generic_success = json_encode(array(
110 'type' => 'success',
111 'msg' => 'Task completed'
112 ));
113 if ($return === false) {
114 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_failure;
115 }
116 else {
117 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_success;
118 }
119 }
120 if (!isset($_POST['attr']) && $category != "fido2-registration") {
121 echo $request_incomplete;
122 exit;
123 }
124 else {
125 if ($category != "fido2-registration") {
126 $attr = (array)json_decode($_POST['attr'], true);
127 }
128 unset($attr['csrf_token']);
129 }
130 // only allow POST requests to POST API endpoints
131 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
132 http_response_code(405);
133 echo json_encode(array(
134 'type' => 'error',
135 'msg' => 'only POST method is allowed'
136 ));
137 exit();
138 }
139
140 switch ($category) {
141 // fido2-registration via POST
142 case "fido2-registration":
143 header('Content-Type: application/json');
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200144 if (isset($_SESSION["mailcow_cc_role"])) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100145 $post = trim(file_get_contents('php://input'));
146 if ($post) {
147 $post = json_decode($post);
148 }
149 $clientDataJSON = base64_decode($post->clientDataJSON);
150 $attestationObject = base64_decode($post->attestationObject);
151 $challenge = $_SESSION['challenge'];
152 try {
153 $data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $challenge, $GLOBALS['FIDO2_UV_FLAG_REGISTER'], $GLOBALS['FIDO2_USER_PRESENT_FLAG']);
154 }
155 catch (Throwable $ex) {
156 $return = new stdClass();
157 $return->success = false;
158 $return->msg = $ex->getMessage();
159 echo json_encode($return);
160 exit;
161 }
162 fido2(array("action" => "register", "registration" => $data));
163 $return = new stdClass();
164 $return->success = true;
165 echo json_encode($return);
166 exit;
167 }
168 else {
169 echo $request_incomplete;
170 exit;
171 }
172 break;
173 case "time_limited_alias":
174 process_add_return(mailbox('add', 'time_limited_alias', $attr));
175 break;
176 case "relayhost":
177 process_add_return(relayhost('add', $attr));
178 break;
179 case "transport":
180 process_add_return(transport('add', $attr));
181 break;
182 case "rsetting":
183 process_add_return(rsettings('add', $attr));
184 break;
185 case "mailbox":
186 process_add_return(mailbox('add', 'mailbox', $attr));
187 break;
188 case "oauth2-client":
189 process_add_return(oauth2('add', 'client', $attr));
190 break;
191 case "domain":
192 process_add_return(mailbox('add', 'domain', $attr));
193 break;
194 case "resource":
195 process_add_return(mailbox('add', 'resource', $attr));
196 break;
197 case "alias":
198 process_add_return(mailbox('add', 'alias', $attr));
199 break;
200 case "filter":
201 process_add_return(mailbox('add', 'filter', $attr));
202 break;
203 case "global-filter":
204 process_add_return(mailbox('add', 'global_filter', $attr));
205 break;
206 case "domain-policy":
207 process_add_return(policy('add', 'domain', $attr));
208 break;
209 case "mailbox-policy":
210 process_add_return(policy('add', 'mailbox', $attr));
211 break;
212 case "alias-domain":
213 process_add_return(mailbox('add', 'alias_domain', $attr));
214 break;
215 case "fwdhost":
216 process_add_return(fwdhost('add', $attr));
217 break;
218 case "dkim":
219 process_add_return(dkim('add', $attr));
220 break;
221 case "dkim_duplicate":
222 process_add_return(dkim('duplicate', $attr));
223 break;
224 case "dkim_import":
225 process_add_return(dkim('import', $attr));
226 break;
227 case "domain-admin":
228 process_add_return(domain_admin('add', $attr));
229 break;
230 case "admin":
231 process_add_return(admin('add', $attr));
232 break;
233 case "syncjob":
234 process_add_return(mailbox('add', 'syncjob', $attr));
235 break;
236 case "bcc":
237 process_add_return(bcc('add', $attr));
238 break;
239 case "recipient_map":
240 process_add_return(recipient_map('add', $attr));
241 break;
242 case "tls-policy-map":
243 process_add_return(tls_policy_maps('add', $attr));
244 break;
245 case "app-passwd":
246 process_add_return(app_passwd('add', $attr));
247 break;
248 // return no route found if no case is matched
249 default:
250 http_response_code(404);
251 echo json_encode(array(
252 'type' => 'error',
253 'msg' => 'route not found'
254 ));
255 exit();
256 }
257 break;
258 case "process":
259 // only allow POST requests to process API endpoints
260 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
261 http_response_code(405);
262 echo json_encode(array(
263 'type' => 'error',
264 'msg' => 'only POST method is allowed'
265 ));
266 exit();
267 }
268 switch ($category) {
269 case "fido2-args":
270 header('Content-Type: application/json');
271 $post = trim(file_get_contents('php://input'));
272 if ($post) {
273 $post = json_decode($post);
274 }
275 $clientDataJSON = base64_decode($post->clientDataJSON);
276 $authenticatorData = base64_decode($post->authenticatorData);
277 $signature = base64_decode($post->signature);
278 $id = base64_decode($post->id);
279 $challenge = $_SESSION['challenge'];
280 $process_fido2 = fido2(array("action" => "get_by_b64cid", "cid" => $post->id));
281 if ($process_fido2['pub_key'] === false) {
282 $return = new stdClass();
283 $return->success = false;
284 echo json_encode($return);
285 exit;
286 }
287 try {
288 $WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_fido2['pub_key'], $challenge, null, $GLOBALS['FIDO2_UV_FLAG_LOGIN'], $GLOBALS['FIDO2_USER_PRESENT_FLAG']);
289 }
290 catch (Throwable $ex) {
291 unset($process_fido2);
292 $return = new stdClass();
293 $return->success = false;
294 echo json_encode($return);
295 exit;
296 }
297 $return = new stdClass();
298 $return->success = true;
299 $stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
300 $stmt->execute(array(':username' => $process_fido2['username']));
301 $obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
302 if ($obj_props['superadmin'] === 1) {
303 $_SESSION["mailcow_cc_role"] = "admin";
304 }
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200305 elseif ($obj_props['superadmin'] === 0) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100306 $_SESSION["mailcow_cc_role"] = "domainadmin";
307 }
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200308 else {
309 $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
310 $stmt->execute(array(':username' => $process_fido2['username']));
311 $row = $stmt->fetch(PDO::FETCH_ASSOC);
312 if ($row['username'] == $process_fido2['username']) {
313 $_SESSION["mailcow_cc_role"] = "user";
314 }
315 }
316 if (empty($_SESSION["mailcow_cc_role"])) {
317 session_unset();
318 session_destroy();
319 exit;
320 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100321 $_SESSION["mailcow_cc_username"] = $process_fido2['username'];
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100322 $_SESSION["fido2_cid"] = $process_fido2['cid'];
323 unset($_SESSION["challenge"]);
324 $_SESSION['return'][] = array(
325 'type' => 'success',
326 'log' => array("fido2_login"),
327 'msg' => array('logged_in_as', $process_fido2['username'])
328 );
329 echo json_encode($return);
330 break;
331 }
332 break;
333 case "get":
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200334 function process_get_return($data, $object = true) {
335 if ($object === true) {
336 $ret_str = '{}';
337 }
338 else {
339 $ret_str = '[]';
340 }
341 echo (!isset($data) || empty($data)) ? $ret_str : json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100342 }
343 // only allow GET requests to GET API endpoints
344 if ($_SERVER['REQUEST_METHOD'] != 'GET') {
345 http_response_code(405);
346 echo json_encode(array(
347 'type' => 'error',
348 'msg' => 'only GET method is allowed'
349 ));
350 exit();
351 }
352 switch ($category) {
353 case "u2f-registration":
354 header('Content-Type: application/javascript');
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200355 if (isset($_SESSION["mailcow_cc_role"]) && $_SESSION["mailcow_cc_username"] == $object) {
356 list($req, $sigs) = $u2f->getRegisterData(get_u2f_registrations($object));
357 $_SESSION['regReq'] = json_encode($req);
358 $_SESSION['regSigs'] = json_encode($sigs);
359 echo 'var req = ' . json_encode($req) . ';';
360 echo 'var registeredKeys = ' . json_encode($sigs) . ';';
361 echo 'var appId = req.appId;';
362 echo 'var registerRequests = [{version: req.version, challenge: req.challenge}];';
363 return;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100364 }
365 else {
366 return;
367 }
368 break;
369 // fido2-registration via GET
370 case "fido2-registration":
371 header('Content-Type: application/json');
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200372 if (isset($_SESSION["mailcow_cc_role"])) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100373 // Exclude existing CredentialIds, if any
374 $excludeCredentialIds = fido2(array("action" => "get_user_cids"));
375 $createArgs = $WebAuthn->getCreateArgs($_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], 30, true, $GLOBALS['FIDO2_UV_FLAG_REGISTER'], $excludeCredentialIds);
376 print(json_encode($createArgs));
377 $_SESSION['challenge'] = $WebAuthn->getChallenge();
378 return;
379 }
380 else {
381 return;
382 }
383 break;
384 case "u2f-authentication":
385 header('Content-Type: application/javascript');
386 if (isset($_SESSION['pending_mailcow_cc_username']) && $_SESSION['pending_mailcow_cc_username'] == $object) {
387 $auth_data = $u2f->getAuthenticateData(get_u2f_registrations($object));
388 $challenge = $auth_data[0]->challenge;
389 $appId = $auth_data[0]->appId;
390 foreach ($auth_data as $each) {
391 $key = array(); // Empty array
392 $key['version'] = $each->version;
393 $key['keyHandle'] = $each->keyHandle;
394 $registeredKey[] = $key;
395 }
396 $_SESSION['authReq'] = json_encode($auth_data);
397 echo 'var appId = "' . $appId . '";';
398 echo 'var challenge = ' . json_encode($challenge) . ';';
399 echo 'var registeredKeys = ' . json_encode($registeredKey) . ';';
400 return;
401 }
402 else {
403 return;
404 }
405 break;
406 case "fido2-get-args":
407 header('Content-Type: application/json');
408 // Login without username, no ids!
409 // $ids = fido2(array("action" => "get_all_cids"));
410 // if (count($ids) == 0) {
411 // return;
412 // }
413 $ids = NULL;
414 $getArgs = $WebAuthn->getGetArgs($ids, 30, true, true, true, true, $GLOBALS['FIDO2_UV_FLAG_LOGIN']);
415 print(json_encode($getArgs));
416 $_SESSION['challenge'] = $WebAuthn->getChallenge();
417 return;
418 break;
419 }
420 if (isset($_SESSION['mailcow_cc_role'])) {
421 switch ($category) {
422 case "rspamd":
423 switch ($object) {
424 case "actions":
425 $data = rspamd_actions();
426 if ($data) {
427 echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
428 }
429 else {
430 echo '{}';
431 }
432 break;
433 }
434 break;
435
436 case "domain":
437 switch ($object) {
438 case "all":
439 $domains = mailbox('get', 'domains');
440 if (!empty($domains)) {
441 foreach ($domains as $domain) {
442 if ($details = mailbox('get', 'domain_details', $domain)) {
443 $data[] = $details;
444 }
445 else {
446 continue;
447 }
448 }
449 process_get_return($data);
450 }
451 else {
452 echo '{}';
453 }
454 break;
455
456 default:
457 $data = mailbox('get', 'domain_details', $object);
458 process_get_return($data);
459 break;
460 }
461 break;
462
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200463 case "passwordpolicy":
464 switch ($object) {
465 case "html":
466 $password_complexity_rules = password_complexity('html');
467 if ($password_complexity_rules !== false) {
468 process_get_return($password_complexity_rules);
469 }
470 else {
471 echo '{}';
472 }
473 break;
474 }
475 break;
476
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100477 case "app-passwd":
478 switch ($object) {
479 case "all":
480 if (empty($extra)) {
481 $app_passwds = app_passwd('get');
482 }
483 else {
484 $app_passwds = app_passwd('get', array('username' => $extra));
485 }
486 if (!empty($app_passwds)) {
487 foreach ($app_passwds as $app_passwd) {
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100488 $details = app_passwd('details', $app_passwd['id']);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100489 if ($details !== false) {
490 $data[] = $details;
491 }
492 else {
493 continue;
494 }
495 }
496 process_get_return($data);
497 }
498 else {
499 echo '{}';
500 }
501 break;
502
503 default:
504 $data = app_passwd('details', array('id' => $object['id']));
505 process_get_return($data);
506 break;
507 }
508 break;
509
510 case "mailq":
511 switch ($object) {
512 case "all":
513 $mailq = mailq('get');
514 if (!empty($mailq)) {
515 echo $mailq;
516 }
517 else {
518 echo '[]';
519 }
520 break;
521 }
522 break;
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100523
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100524 case "postcat":
525 switch ($object) {
526 default:
527 $data = mailq('cat', array('qid' => $object));
528 echo $data;
529 break;
530 }
531 break;
532
533 case "global_filters":
534 $global_filters = mailbox('get', 'global_filter_details');
535 switch ($object) {
536 case "all":
537 if (!empty($global_filters)) {
538 process_get_return($global_filters);
539 }
540 else {
541 echo '{}';
542 }
543 break;
544 case "prefilter":
545 if (!empty($global_filters['prefilter'])) {
546 process_get_return($global_filters['prefilter']);
547 }
548 else {
549 echo '{}';
550 }
551 break;
552 case "postfilter":
553 if (!empty($global_filters['postfilter'])) {
554 process_get_return($global_filters['postfilter']);
555 }
556 else {
557 echo '{}';
558 }
559 break;
560 }
561 break;
562
563 case "rl-domain":
564 switch ($object) {
565 case "all":
566 $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
567 if (!empty($domains)) {
568 foreach ($domains as $domain) {
569 if ($details = ratelimit('get', 'domain', $domain)) {
570 $details['domain'] = $domain;
571 $data[] = $details;
572 }
573 else {
574 continue;
575 }
576 }
577 process_get_return($data);
578 }
579 else {
580 echo '{}';
581 }
582 break;
583
584 default:
585 $data = ratelimit('get', 'domain', $object);
586 process_get_return($data);
587 break;
588 }
589 break;
590
591 case "rl-mbox":
592 switch ($object) {
593 case "all":
594 $domains = mailbox('get', 'domains');
595 if (!empty($domains)) {
596 foreach ($domains as $domain) {
597 $mailboxes = mailbox('get', 'mailboxes', $domain);
598 if (!empty($mailboxes)) {
599 foreach ($mailboxes as $mailbox) {
600 if ($details = ratelimit('get', 'mailbox', $mailbox)) {
601 $details['mailbox'] = $mailbox;
602 $data[] = $details;
603 }
604 else {
605 continue;
606 }
607 }
608 }
609 }
610 process_get_return($data);
611 }
612 else {
613 echo '{}';
614 }
615 break;
616
617 default:
618 $data = ratelimit('get', 'mailbox', $object);
619 process_get_return($data);
620 break;
621 }
622 break;
623
624 case "relayhost":
625 switch ($object) {
626 case "all":
627 $relayhosts = relayhost('get');
628 if (!empty($relayhosts)) {
629 foreach ($relayhosts as $relayhost) {
630 if ($details = relayhost('details', $relayhost['id'])) {
631 $data[] = $details;
632 }
633 else {
634 continue;
635 }
636 }
637 process_get_return($data);
638 }
639 else {
640 echo '{}';
641 }
642 break;
643
644 default:
645 $data = relayhost('details', $object);
646 process_get_return($data);
647 break;
648 }
649 break;
650
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200651 case "last-login":
652 if ($object) {
653 // extra == days
654 if (isset($extra) && intval($extra) >= 1) {
655 $data = last_login('get', $object, intval($extra));
656 }
657 else {
658 $data = last_login('get', $object);
659 }
660 process_get_return($data);
661 }
662 break;
663
664 // Todo: move to delete
665 case "reset-last-login":
666 if ($object) {
667 $data = last_login('reset', $object);
668 process_get_return($data);
669 }
670 break;
671
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100672 case "transport":
673 switch ($object) {
674 case "all":
675 $transports = transport('get');
676 if (!empty($transports)) {
677 foreach ($transports as $transport) {
678 if ($details = transport('details', $transport['id'])) {
679 $data[] = $details;
680 }
681 else {
682 continue;
683 }
684 }
685 process_get_return($data);
686 }
687 else {
688 echo '{}';
689 }
690 break;
691
692 default:
693 $data = transport('details', $object);
694 process_get_return($data);
695 break;
696 }
697 break;
698
699 case "rsetting":
700 switch ($object) {
701 case "all":
702 $rsettings = rsettings('get');
703 if (!empty($rsettings)) {
704 foreach ($rsettings as $rsetting) {
705 if ($details = rsettings('details', $rsetting['id'])) {
706 $data[] = $details;
707 }
708 else {
709 continue;
710 }
711 }
712 process_get_return($data);
713 }
714 else {
715 echo '{}';
716 }
717 break;
718
719 default:
720 $data = rsettings('details', $object);
721 process_get_return($data);
722 break;
723 }
724 break;
725
726 case "oauth2-client":
727 switch ($object) {
728 case "all":
729 $clients = oauth2('get', 'clients');
730 if (!empty($clients)) {
731 foreach ($clients as $client) {
732 if ($details = oauth2('details', 'client', $client)) {
733 $data[] = $details;
734 }
735 else {
736 continue;
737 }
738 }
739 process_get_return($data);
740 }
741 else {
742 echo '{}';
743 }
744 break;
745
746 default:
747 $data = oauth2('details', 'client', $object);
748 process_get_return($data);
749 break;
750 }
751 break;
752
753 case "logs":
754 switch ($object) {
755 case "dovecot":
756 // 0 is first record, so empty is fine
757 if (isset($extra)) {
758 $extra = preg_replace('/[^\d\-]/i', '', $extra);
759 $logs = get_logs('dovecot-mailcow', $extra);
760 }
761 else {
762 $logs = get_logs('dovecot-mailcow');
763 }
764 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
765 break;
766 case "ratelimited":
767 // 0 is first record, so empty is fine
768 if (isset($extra)) {
769 $extra = preg_replace('/[^\d\-]/i', '', $extra);
770 $logs = get_logs('ratelimited', $extra);
771 }
772 else {
773 $logs = get_logs('ratelimited');
774 }
775 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
776 break;
777 case "netfilter":
778 // 0 is first record, so empty is fine
779 if (isset($extra)) {
780 $extra = preg_replace('/[^\d\-]/i', '', $extra);
781 $logs = get_logs('netfilter-mailcow', $extra);
782 }
783 else {
784 $logs = get_logs('netfilter-mailcow');
785 }
786 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
787 break;
788 case "postfix":
789 // 0 is first record, so empty is fine
790 if (isset($extra)) {
791 $extra = preg_replace('/[^\d\-]/i', '', $extra);
792 $logs = get_logs('postfix-mailcow', $extra);
793 }
794 else {
795 $logs = get_logs('postfix-mailcow');
796 }
797 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
798 break;
799 case "autodiscover":
800 // 0 is first record, so empty is fine
801 if (isset($extra)) {
802 $extra = preg_replace('/[^\d\-]/i', '', $extra);
803 $logs = get_logs('autodiscover-mailcow', $extra);
804 }
805 else {
806 $logs = get_logs('autodiscover-mailcow');
807 }
808 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
809 break;
810 case "sogo":
811 // 0 is first record, so empty is fine
812 if (isset($extra)) {
813 $extra = preg_replace('/[^\d\-]/i', '', $extra);
814 $logs = get_logs('sogo-mailcow', $extra);
815 }
816 else {
817 $logs = get_logs('sogo-mailcow');
818 }
819 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
820 break;
821 case "ui":
822 // 0 is first record, so empty is fine
823 if (isset($extra)) {
824 $extra = preg_replace('/[^\d\-]/i', '', $extra);
825 $logs = get_logs('mailcow-ui', $extra);
826 }
827 else {
828 $logs = get_logs('mailcow-ui');
829 }
830 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
831 break;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200832 case "sasl":
833 // 0 is first record, so empty is fine
834 if (isset($extra)) {
835 $extra = preg_replace('/[^\d\-]/i', '', $extra);
836 $logs = get_logs('sasl', $extra);
837 }
838 else {
839 $logs = get_logs('sasl');
840 }
841 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
842 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100843 case "watchdog":
844 // 0 is first record, so empty is fine
845 if (isset($extra)) {
846 $extra = preg_replace('/[^\d\-]/i', '', $extra);
847 $logs = get_logs('watchdog-mailcow', $extra);
848 }
849 else {
850 $logs = get_logs('watchdog-mailcow');
851 }
852 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
853 break;
854 case "acme":
855 // 0 is first record, so empty is fine
856 if (isset($extra)) {
857 $extra = preg_replace('/[^\d\-]/i', '', $extra);
858 $logs = get_logs('acme-mailcow', $extra);
859 }
860 else {
861 $logs = get_logs('acme-mailcow');
862 }
863 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
864 break;
865 case "api":
866 // 0 is first record, so empty is fine
867 if (isset($extra)) {
868 $extra = preg_replace('/[^\d\-]/i', '', $extra);
869 $logs = get_logs('api-mailcow', $extra);
870 }
871 else {
872 $logs = get_logs('api-mailcow');
873 }
874 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
875 break;
876 case "rspamd-history":
877 // 0 is first record, so empty is fine
878 if (isset($extra)) {
879 $extra = preg_replace('/[^\d\-]/i', '', $extra);
880 $logs = get_logs('rspamd-history', $extra);
881 }
882 else {
883 $logs = get_logs('rspamd-history');
884 }
885 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
886 break;
887 case "rspamd-stats":
888 $logs = get_logs('rspamd-stats');
889 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
890 break;
891 // return no route found if no case is matched
892 default:
893 http_response_code(404);
894 echo json_encode(array(
895 'type' => 'error',
896 'msg' => 'route not found'
897 ));
898 exit();
899 }
900 break;
901 case "mailbox":
902 switch ($object) {
903 case "all":
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200904 case "reduced":
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100905 if (empty($extra)) {
906 $domains = mailbox('get', 'domains');
907 }
908 else {
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200909 $domains = explode(',', $extra);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100910 }
911 if (!empty($domains)) {
912 foreach ($domains as $domain) {
913 $mailboxes = mailbox('get', 'mailboxes', $domain);
914 if (!empty($mailboxes)) {
915 foreach ($mailboxes as $mailbox) {
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200916 if ($details = mailbox('get', 'mailbox_details', $mailbox, $object)) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100917 $data[] = $details;
918 }
919 else {
920 continue;
921 }
922 }
923 }
924 }
925 process_get_return($data);
926 }
927 else {
928 echo '{}';
929 }
930 break;
931
932 default:
933 $data = mailbox('get', 'mailbox_details', $object);
934 process_get_return($data);
935 break;
936 }
937 break;
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100938 case "bcc-destination-options":
939 $domains = mailbox('get', 'domains');
940 $alias_domains = mailbox('get', 'alias_domains');
941 $data = array();
942 if (!empty($domains)) {
943 foreach ($domains as $domain) {
944 $data['domains'][] = $domain;
945 $mailboxes = mailbox('get', 'mailboxes', $domain);
946 foreach ($mailboxes as $mailbox) {
947 $data['mailboxes'][$mailbox][] = $mailbox;
948 $user_alias_details = user_get_alias_details($mailbox);
949 foreach ($user_alias_details['direct_aliases'] as $k => $v) {
950 $data['mailboxes'][$mailbox][] = $k;
951 }
952 foreach ($user_alias_details['shared_aliases'] as $k => $v) {
953 $data['mailboxes'][$mailbox][] = $k;
954 }
955 }
956 }
957 }
958 if (!empty($alias_domains)) {
959 foreach ($alias_domains as $alias_domain) {
960 $data['alias_domains'][] = $alias_domain;
961 }
962 }
963 process_get_return($data);
964 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100965 case "syncjobs":
966 switch ($object) {
967 case "all":
968 $domains = mailbox('get', 'domains');
969 if (!empty($domains)) {
970 foreach ($domains as $domain) {
971 $mailboxes = mailbox('get', 'mailboxes', $domain);
972 if (!empty($mailboxes)) {
973 foreach ($mailboxes as $mailbox) {
974 $syncjobs = mailbox('get', 'syncjobs', $mailbox);
975 if (!empty($syncjobs)) {
976 foreach ($syncjobs as $syncjob) {
977 if (isset($extra)) {
978 $details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra));
979 }
980 else {
981 $details = mailbox('get', 'syncjob_details', $syncjob);
982 }
983 if ($details) {
984 $data[] = $details;
985 }
986 else {
987 continue;
988 }
989 }
990 }
991 }
992 }
993 }
994 process_get_return($data);
995 }
996 else {
997 echo '{}';
998 }
999 break;
1000
1001 default:
1002 $syncjobs = mailbox('get', 'syncjobs', $object);
1003 if (!empty($syncjobs)) {
1004 foreach ($syncjobs as $syncjob) {
1005 if (isset($extra)) {
1006 $details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra));
1007 }
1008 else {
1009 $details = mailbox('get', 'syncjob_details', $syncjob);
1010 }
1011 if ($details) {
1012 $data[] = $details;
1013 }
1014 else {
1015 continue;
1016 }
1017 }
1018 }
1019 process_get_return($data);
1020 break;
1021 }
1022 break;
1023 case "active-user-sieve":
1024 if (isset($object)) {
1025 $sieve_filter = mailbox('get', 'active_user_sieve', $object);
1026 if (!empty($sieve_filter)) {
1027 $data[] = $sieve_filter;
1028 }
1029 }
1030 process_get_return($data);
1031 break;
1032 case "filters":
1033 switch ($object) {
1034 case "all":
1035 $domains = mailbox('get', 'domains');
1036 if (!empty($domains)) {
1037 foreach ($domains as $domain) {
1038 $mailboxes = mailbox('get', 'mailboxes', $domain);
1039 if (!empty($mailboxes)) {
1040 foreach ($mailboxes as $mailbox) {
1041 $filters = mailbox('get', 'filters', $mailbox);
1042 if (!empty($filters)) {
1043 foreach ($filters as $filter) {
1044 if ($details = mailbox('get', 'filter_details', $filter)) {
1045 $data[] = $details;
1046 }
1047 else {
1048 continue;
1049 }
1050 }
1051 }
1052 }
1053 }
1054 }
1055 process_get_return($data);
1056 }
1057 else {
1058 echo '{}';
1059 }
1060 break;
1061
1062 default:
1063 $filters = mailbox('get', 'filters', $object);
1064 if (!empty($filters)) {
1065 foreach ($filters as $filter) {
1066 if ($details = mailbox('get', 'filter_details', $filter)) {
1067 $data[] = $details;
1068 }
1069 else {
1070 continue;
1071 }
1072 }
1073 }
1074 process_get_return($data);
1075 break;
1076 }
1077 break;
1078 case "bcc":
1079 switch ($object) {
1080 case "all":
1081 $bcc_items = bcc('get');
1082 if (!empty($bcc_items)) {
1083 foreach ($bcc_items as $bcc_item) {
1084 if ($details = bcc('details', $bcc_item)) {
1085 $data[] = $details;
1086 }
1087 else {
1088 continue;
1089 }
1090 }
1091 }
1092 process_get_return($data);
1093 break;
1094 default:
1095 $data = bcc('details', $object);
1096 if (!empty($data)) {
1097 $data[] = $details;
1098 }
1099 process_get_return($data);
1100 break;
1101 }
1102 break;
1103 case "recipient_map":
1104 switch ($object) {
1105 case "all":
1106 $recipient_map_items = recipient_map('get');
1107 if (!empty($recipient_map_items)) {
1108 foreach ($recipient_map_items as $recipient_map_item) {
1109 if ($details = recipient_map('details', $recipient_map_item)) {
1110 $data[] = $details;
1111 }
1112 else {
1113 continue;
1114 }
1115 }
1116 }
1117 process_get_return($data);
1118 break;
1119 default:
1120 $data = recipient_map('details', $object);
1121 if (!empty($data)) {
1122 $data[] = $details;
1123 }
1124 process_get_return($data);
1125 break;
1126 }
1127 break;
1128 case "tls-policy-map":
1129 switch ($object) {
1130 case "all":
1131 $tls_policy_maps_items = tls_policy_maps('get');
1132 if (!empty($tls_policy_maps_items)) {
1133 foreach ($tls_policy_maps_items as $tls_policy_maps_item) {
1134 if ($details = tls_policy_maps('details', $tls_policy_maps_item)) {
1135 $data[] = $details;
1136 }
1137 else {
1138 continue;
1139 }
1140 }
1141 }
1142 process_get_return($data);
1143 break;
1144 default:
1145 $data = tls_policy_maps('details', $object);
1146 if (!empty($data)) {
1147 $data[] = $details;
1148 }
1149 process_get_return($data);
1150 break;
1151 }
1152 break;
1153 case "policy_wl_mailbox":
1154 switch ($object) {
1155 default:
1156 $data = policy('get', 'mailbox', $object)['whitelist'];
1157 process_get_return($data);
1158 break;
1159 }
1160 break;
1161 case "policy_bl_mailbox":
1162 switch ($object) {
1163 default:
1164 $data = policy('get', 'mailbox', $object)['blacklist'];
1165 process_get_return($data);
1166 break;
1167 }
1168 break;
1169 case "policy_wl_domain":
1170 switch ($object) {
1171 default:
1172 $data = policy('get', 'domain', $object)['whitelist'];
1173 process_get_return($data);
1174 break;
1175 }
1176 break;
1177 case "policy_bl_domain":
1178 switch ($object) {
1179 default:
1180 $data = policy('get', 'domain', $object)['blacklist'];
1181 process_get_return($data);
1182 break;
1183 }
1184 break;
1185 case "time_limited_aliases":
1186 switch ($object) {
1187 default:
1188 $data = mailbox('get', 'time_limited_aliases', $object);
1189 process_get_return($data);
1190 break;
1191 }
1192 break;
1193 case "fail2ban":
1194 switch ($object) {
1195 default:
1196 $data = fail2ban('get');
1197 process_get_return($data);
1198 break;
1199 }
1200 break;
1201 case "resource":
1202 switch ($object) {
1203 case "all":
1204 $domains = mailbox('get', 'domains');
1205 if (!empty($domains)) {
1206 foreach ($domains as $domain) {
1207 $resources = mailbox('get', 'resources', $domain);
1208 if (!empty($resources)) {
1209 foreach ($resources as $resource) {
1210 if ($details = mailbox('get', 'resource_details', $resource)) {
1211 $data[] = $details;
1212 }
1213 else {
1214 continue;
1215 }
1216 }
1217 }
1218 }
1219 process_get_return($data);
1220 }
1221 else {
1222 echo '{}';
1223 }
1224 break;
1225 default:
1226 $data = mailbox('get', 'resource_details', $object);
1227 process_get_return($data);
1228 break;
1229 }
1230 break;
1231 case "fwdhost":
1232 switch ($object) {
1233 case "all":
1234 process_get_return(fwdhost('get'));
1235 break;
1236 default:
1237 process_get_return(fwdhost('details', $object));
1238 break;
1239 }
1240 break;
1241 case "quarantine":
1242 // "all" will not print details
1243 switch ($object) {
1244 case "all":
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001245 process_get_return(quarantine('get'), false);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001246 break;
1247 default:
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001248 process_get_return(quarantine('details', $object), false);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001249 break;
1250 }
1251 break;
1252 case "alias-domain":
1253 switch ($object) {
1254 case "all":
1255 $alias_domains = mailbox('get', 'alias_domains');
1256 if (!empty($alias_domains)) {
1257 foreach ($alias_domains as $alias_domain) {
1258 if ($details = mailbox('get', 'alias_domain_details', $alias_domain)) {
1259 $data[] = $details;
1260 }
1261 else {
1262 continue;
1263 }
1264 }
1265 }
1266 process_get_return($data);
1267 break;
1268 default:
1269 process_get_return(mailbox('get', 'alias_domain_details', $object));
1270 break;
1271 }
1272 break;
1273 case "alias":
1274 switch ($object) {
1275 case "all":
1276 if (empty($extra)) {
1277 $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
1278 }
1279 else {
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001280 $domains = explode(',', $extra);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001281 }
1282 if (!empty($domains)) {
1283 foreach ($domains as $domain) {
1284 $aliases = mailbox('get', 'aliases', $domain);
1285 if (!empty($aliases)) {
1286 foreach ($aliases as $alias) {
1287 if ($details = mailbox('get', 'alias_details', $alias)) {
1288 $data[] = $details;
1289 }
1290 else {
1291 continue;
1292 }
1293 }
1294 }
1295 }
1296 process_get_return($data);
1297 }
1298 else {
1299 echo '{}';
1300 }
1301 break;
1302
1303 default:
1304 process_get_return(mailbox('get', 'alias_details', $object));
1305 break;
1306 }
1307 break;
1308 case "domain-admin":
1309 switch ($object) {
1310 case "all":
1311 $domain_admins = domain_admin('get');
1312 if (!empty($domain_admins)) {
1313 foreach ($domain_admins as $domain_admin) {
1314 if ($details = domain_admin('details', $domain_admin)) {
1315 $data[] = $details;
1316 }
1317 else {
1318 continue;
1319 }
1320 }
1321 process_get_return($data);
1322 }
1323 else {
1324 echo '{}';
1325 }
1326 break;
1327
1328 default:
1329 process_get_return(domain_admin('details', $object));
1330 break;
1331 }
1332 break;
1333 case "admin":
1334 switch ($object) {
1335 case "all":
1336 $admins = admin('get');
1337 if (!empty($admins)) {
1338 foreach ($admins as $admin) {
1339 if ($details = admin('details', $admin)) {
1340 $data[] = $details;
1341 }
1342 else {
1343 continue;
1344 }
1345 }
1346 process_get_return($data);
1347 }
1348 else {
1349 echo '{}';
1350 }
1351 break;
1352
1353 default:
1354 process_get_return(admin('details', $object));
1355 break;
1356 }
1357 break;
1358 case "dkim":
1359 switch ($object) {
1360 default:
1361 $data = dkim('details', $object);
1362 process_get_return($data);
1363 break;
1364 }
1365 break;
1366 case "presets":
1367 switch ($object) {
1368 case "rspamd":
1369 process_get_return(presets('get', 'rspamd'));
1370 break;
1371 case "sieve":
1372 process_get_return(presets('get', 'sieve'));
1373 break;
1374 }
1375 break;
1376 case "status":
1377 if ($_SESSION['mailcow_cc_role'] == "admin") {
1378 switch ($object) {
1379 case "containers":
1380 $containers = (docker('info'));
1381 foreach ($containers as $container => $container_info) {
1382 $container . ' (' . $container_info['Config']['Image'] . ')';
1383 $containerstarttime = ($container_info['State']['StartedAt']);
1384 $containerstate = ($container_info['State']['Status']);
1385 $containerimage = ($container_info['Config']['Image']);
1386 $temp[$container] = array(
1387 'type' => 'info',
1388 'container' => $container,
1389 'state' => $containerstate,
1390 'started_at' => $containerstarttime,
1391 'image' => $containerimage
1392 );
1393 }
1394 echo json_encode($temp, JSON_UNESCAPED_SLASHES);
1395 break;
1396 case "vmail":
1397 $exec_fields_vmail = array('cmd' => 'system', 'task' => 'df', 'dir' => '/var/vmail');
1398 $vmail_df = explode(',', json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields_vmail), true));
1399 $temp = array(
1400 'type' => 'info',
1401 'disk' => $vmail_df[0],
1402 'used' => $vmail_df[2],
1403 'total'=> $vmail_df[1],
1404 'used_percent' => $vmail_df[4]
1405 );
1406 echo json_encode($temp, JSON_UNESCAPED_SLASHES);
1407 break;
1408 case "solr":
1409 $solr_status = solr_status();
1410 $solr_size = ($solr_status['status']['dovecot-fts']['index']['size']);
1411 $solr_documents = ($solr_status['status']['dovecot-fts']['index']['numDocs']);
1412 if (strtolower(getenv('SKIP_SOLR')) != 'n') {
1413 $solr_enabled = false;
1414 }
1415 else {
1416 $solr_enabled = true;
1417 }
1418 echo json_encode(array(
1419 'type' => 'info',
1420 'solr_enabled' => $solr_enabled,
1421 'solr_size' => $solr_size,
1422 'solr_documents' => $solr_documents
1423 ));
1424 break;
1425 }
1426 }
1427 break;
1428 break;
1429 // return no route found if no case is matched
1430 default:
1431 http_response_code(404);
1432 echo json_encode(array(
1433 'type' => 'error',
1434 'msg' => 'route not found'
1435 ));
1436 exit();
1437 }
1438 }
1439 break;
1440 case "delete":
1441 if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username']) || !isset($_SESSION["mailcow_cc_username"])) {
1442 http_response_code(403);
1443 echo json_encode(array(
1444 'type' => 'error',
1445 'msg' => 'API read/write access denied'
1446 ));
1447 exit();
1448 }
1449 function process_delete_return($return) {
1450 $generic_failure = json_encode(array(
1451 'type' => 'error',
1452 'msg' => 'Cannot delete item'
1453 ));
1454 $generic_success = json_encode(array(
1455 'type' => 'success',
1456 'msg' => 'Task completed'
1457 ));
1458 if ($return === false) {
1459 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_failure;
1460 }
1461 else {
1462 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_success;
1463 }
1464 }
1465 if (!isset($_POST['items'])) {
1466 echo $request_incomplete;
1467 exit;
1468 }
1469 else {
1470 $items = (array)json_decode($_POST['items'], true);
1471 }
1472 // only allow POST requests to POST API endpoints
1473 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
1474 http_response_code(405);
1475 echo json_encode(array(
1476 'type' => 'error',
1477 'msg' => 'only POST method is allowed'
1478 ));
1479 exit();
1480 }
1481 switch ($category) {
1482 case "alias":
1483 process_delete_return(mailbox('delete', 'alias', array('id' => $items)));
1484 break;
1485 case "oauth2-client":
1486 process_delete_return(oauth2('delete', 'client', array('id' => $items)));
1487 break;
1488 case "app-passwd":
1489 process_delete_return(app_passwd('delete', array('id' => $items)));
1490 break;
1491 case "relayhost":
1492 process_delete_return(relayhost('delete', array('id' => $items)));
1493 break;
1494 case "transport":
1495 process_delete_return(transport('delete', array('id' => $items)));
1496 break;
1497 case "rsetting":
1498 process_delete_return(rsettings('delete', array('id' => $items)));
1499 break;
1500 case "syncjob":
1501 process_delete_return(mailbox('delete', 'syncjob', array('id' => $items)));
1502 break;
1503 case "filter":
1504 process_delete_return(mailbox('delete', 'filter', array('id' => $items)));
1505 break;
1506 case "mailq":
1507 process_delete_return(mailq('delete', array('qid' => $items)));
1508 break;
1509 case "qitem":
1510 process_delete_return(quarantine('delete', array('id' => $items)));
1511 break;
1512 case "bcc":
1513 process_delete_return(bcc('delete', array('id' => $items)));
1514 break;
1515 case "recipient_map":
1516 process_delete_return(recipient_map('delete', array('id' => $items)));
1517 break;
1518 case "tls-policy-map":
1519 process_delete_return(tls_policy_maps('delete', array('id' => $items)));
1520 break;
1521 case "fwdhost":
1522 process_delete_return(fwdhost('delete', array('forwardinghost' => $items)));
1523 break;
1524 case "dkim":
1525 process_delete_return(dkim('delete', array('domains' => $items)));
1526 break;
1527 case "domain":
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001528 process_delete_return(mailbox('delete', 'domain', array('domain' => $items)));
1529 break;
1530 case "alias-domain":
1531 process_delete_return(mailbox('delete', 'alias_domain', array('alias_domain' => $items)));
1532 break;
1533 case "mailbox":
1534 process_delete_return(mailbox('delete', 'mailbox', array('username' => $items)));
1535 break;
1536 case "resource":
1537 process_delete_return(mailbox('delete', 'resource', array('name' => $items)));
1538 break;
1539 case "mailbox-policy":
1540 process_delete_return(policy('delete', 'mailbox', array('prefid' => $items)));
1541 break;
1542 case "domain-policy":
1543 process_delete_return(policy('delete', 'domain', array('prefid' => $items)));
1544 break;
1545 case "time_limited_alias":
1546 process_delete_return(mailbox('delete', 'time_limited_alias', array('address' => $items)));
1547 break;
1548 case "eas_cache":
1549 process_delete_return(mailbox('delete', 'eas_cache', array('username' => $items)));
1550 break;
1551 case "sogo_profile":
1552 process_delete_return(mailbox('delete', 'sogo_profile', array('username' => $items)));
1553 break;
1554 case "domain-admin":
1555 process_delete_return(domain_admin('delete', array('username' => $items)));
1556 break;
1557 case "admin":
1558 process_delete_return(admin('delete', array('username' => $items)));
1559 break;
1560 case "rlhash":
1561 echo ratelimit('delete', null, implode($items));
1562 break;
1563 // return no route found if no case is matched
1564 default:
1565 http_response_code(404);
1566 echo json_encode(array(
1567 'type' => 'error',
1568 'msg' => 'route not found'
1569 ));
1570 exit();
1571 }
1572 break;
1573 case "edit":
1574 if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username']) || !isset($_SESSION["mailcow_cc_username"])) {
1575 http_response_code(403);
1576 echo json_encode(array(
1577 'type' => 'error',
1578 'msg' => 'API read/write access denied'
1579 ));
1580 exit();
1581 }
1582 function process_edit_return($return) {
1583 $generic_failure = json_encode(array(
1584 'type' => 'error',
1585 'msg' => 'Cannot edit item'
1586 ));
1587 $generic_success = json_encode(array(
1588 'type' => 'success',
1589 'msg' => 'Task completed'
1590 ));
1591 if ($return === false) {
1592 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_failure;
1593 }
1594 else {
1595 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_success;
1596 }
1597 }
1598 if (!isset($_POST['attr'])) {
1599 echo $request_incomplete;
1600 exit;
1601 }
1602 else {
1603 $attr = (array)json_decode($_POST['attr'], true);
1604 unset($attr['csrf_token']);
1605 $items = isset($_POST['items']) ? (array)json_decode($_POST['items'], true) : null;
1606 }
1607 // only allow POST requests to POST API endpoints
1608 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
1609 http_response_code(405);
1610 echo json_encode(array(
1611 'type' => 'error',
1612 'msg' => 'only POST method is allowed'
1613 ));
1614 exit();
1615 }
1616 switch ($category) {
1617 case "bcc":
1618 process_edit_return(bcc('edit', array_merge(array('id' => $items), $attr)));
1619 break;
1620 case "pushover":
1621 process_edit_return(pushover('edit', array_merge(array('username' => $items), $attr)));
1622 break;
1623 case "pushover-test":
1624 process_edit_return(pushover('test', array_merge(array('username' => $items), $attr)));
1625 break;
1626 case "oauth2-client":
1627 process_edit_return(oauth2('edit', 'client', array_merge(array('id' => $items), $attr)));
1628 break;
1629 case "recipient_map":
1630 process_edit_return(recipient_map('edit', array_merge(array('id' => $items), $attr)));
1631 break;
1632 case "app-passwd":
1633 process_edit_return(app_passwd('edit', array_merge(array('id' => $items), $attr)));
1634 break;
1635 case "tls-policy-map":
1636 process_edit_return(tls_policy_maps('edit', array_merge(array('id' => $items), $attr)));
1637 break;
1638 case "alias":
1639 process_edit_return(mailbox('edit', 'alias', array_merge(array('id' => $items), $attr)));
1640 break;
1641 case "rspamd-map":
1642 process_edit_return(rspamd_maps('edit', array_merge(array('map' => $items), $attr)));
1643 break;
1644 case "fido2-fn":
1645 process_edit_return(fido2(array('action' => 'edit_fn', 'fido2_attrs' => $attr)));
1646 break;
1647 case "app_links":
1648 process_edit_return(customize('edit', 'app_links', $attr));
1649 break;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001650 case "passwordpolicy":
1651 process_edit_return(password_complexity('edit', $attr));
1652 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001653 case "relayhost":
1654 process_edit_return(relayhost('edit', array_merge(array('id' => $items), $attr)));
1655 break;
1656 case "transport":
1657 process_edit_return(transport('edit', array_merge(array('id' => $items), $attr)));
1658 break;
1659 case "rsetting":
1660 process_edit_return(rsettings('edit', array_merge(array('id' => $items), $attr)));
1661 break;
1662 case "delimiter_action":
1663 process_edit_return(mailbox('edit', 'delimiter_action', array_merge(array('username' => $items), $attr)));
1664 break;
1665 case "tls_policy":
1666 process_edit_return(mailbox('edit', 'tls_policy', array_merge(array('username' => $items), $attr)));
1667 break;
1668 case "quarantine_notification":
1669 process_edit_return(mailbox('edit', 'quarantine_notification', array_merge(array('username' => $items), $attr)));
1670 break;
1671 case "quarantine_category":
1672 process_edit_return(mailbox('edit', 'quarantine_category', array_merge(array('username' => $items), $attr)));
1673 break;
1674 case "qitem":
1675 process_edit_return(quarantine('edit', array_merge(array('id' => $items), $attr)));
1676 break;
1677 case "quarantine":
1678 process_edit_return(quarantine('edit', $attr));
1679 break;
1680 case "quota_notification":
1681 process_edit_return(quota_notification('edit', $attr));
1682 break;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001683 case "quota_notification_bcc":
1684 process_edit_return(quota_notification_bcc('edit', $attr));
1685 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001686 case "mailq":
1687 process_edit_return(mailq('edit', array_merge(array('qid' => $items), $attr)));
1688 break;
1689 case "time_limited_alias":
1690 process_edit_return(mailbox('edit', 'time_limited_alias', array_merge(array('address' => $items), $attr)));
1691 break;
1692 case "mailbox":
1693 process_edit_return(mailbox('edit', 'mailbox', array_merge(array('username' => $items), $attr)));
1694 break;
1695 case "syncjob":
1696 process_edit_return(mailbox('edit', 'syncjob', array_merge(array('id' => $items), $attr)));
1697 break;
1698 case "filter":
1699 process_edit_return(mailbox('edit', 'filter', array_merge(array('id' => $items), $attr)));
1700 break;
1701 case "resource":
1702 process_edit_return(mailbox('edit', 'resource', array_merge(array('name' => $items), $attr)));
1703 break;
1704 case "domain":
1705 process_edit_return(mailbox('edit', 'domain', array_merge(array('domain' => $items), $attr)));
1706 break;
1707 case "rl-domain":
1708 process_edit_return(ratelimit('edit', 'domain', array_merge(array('object' => $items), $attr)));
1709 break;
1710 case "rl-mbox":
1711 process_edit_return(ratelimit('edit', 'mailbox', array_merge(array('object' => $items), $attr)));
1712 break;
1713 case "user-acl":
1714 process_edit_return(acl('edit', 'user', array_merge(array('username' => $items), $attr)));
1715 break;
1716 case "da-acl":
1717 process_edit_return(acl('edit', 'domainadmin', array_merge(array('username' => $items), $attr)));
1718 break;
1719 case "alias-domain":
1720 process_edit_return(mailbox('edit', 'alias_domain', array_merge(array('alias_domain' => $items), $attr)));
1721 break;
1722 case "spam-score":
1723 process_edit_return(mailbox('edit', 'spam_score', array_merge(array('username' => $items), $attr)));
1724 break;
1725 case "domain-admin":
1726 process_edit_return(domain_admin('edit', array_merge(array('username' => $items), $attr)));
1727 break;
1728 case "admin":
1729 process_edit_return(admin('edit', array_merge(array('username' => $items), $attr)));
1730 break;
1731 case "fwdhost":
1732 process_edit_return(fwdhost('edit', array_merge(array('fwdhost' => $items), $attr)));
1733 break;
1734 case "fail2ban":
1735 process_edit_return(fail2ban('edit', array_merge(array('network' => $items), $attr)));
1736 break;
1737 case "ui_texts":
1738 process_edit_return(customize('edit', 'ui_texts', $attr));
1739 break;
1740 case "self":
1741 if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
1742 process_edit_return(domain_admin('edit', $attr));
1743 }
1744 elseif ($_SESSION['mailcow_cc_role'] == "user") {
1745 process_edit_return(edit_user_account($attr));
1746 }
1747 break;
1748 // return no route found if no case is matched
1749 default:
1750 http_response_code(404);
1751 echo json_encode(array(
1752 'type' => 'error',
1753 'msg' => 'route not found'
1754 ));
1755 exit();
1756 }
1757 break;
1758 // return no route found if no case is matched
1759 default:
1760 http_response_code(404);
1761 echo json_encode(array(
1762 'type' => 'error',
1763 'msg' => 'route not found'
1764 ));
1765 exit();
1766 }
1767}
1768if ($_SESSION['mailcow_cc_api'] === true) {
1769 if (isset($_SESSION['mailcow_cc_api']) && $_SESSION['mailcow_cc_api'] === true) {
1770 unset($_SESSION['return']);
1771 }
1772}