blob: 79b6bfd53e4cbc6909c76c4f00f12012f395b465 [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 }
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010017
18 $value = json_decode($value, true);
19 if ($value) {
20 if (is_array($value)) unset($value["csrf_token"]);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010021 foreach ($value as $key => &$val) {
22 if(preg_match("/pass/i", $key)) {
23 $val = '*';
24 }
25 }
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010026 $value = json_encode($value);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010027 }
28 $data_var[] = $data . "='" . $value . "'";
29 }
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010030
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010031 try {
32 $log_line = array(
33 'time' => time(),
34 'uri' => $_SERVER['REQUEST_URI'],
35 'method' => $_SERVER['REQUEST_METHOD'],
36 'remote' => get_remote_ip(),
37 'data' => implode(', ', $data_var)
38 );
39 $redis->lPush('API_LOG', json_encode($log_line));
40 }
41 catch (RedisException $e) {
42 $_SESSION['return'][] = array(
43 'type' => 'danger',
44 'msg' => 'Redis: '.$e
45 );
46 return false;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010047 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010048}
49
50if (isset($_GET['query'])) {
51
52 $query = explode('/', $_GET['query']);
53 $action = (isset($query[0])) ? $query[0] : null;
54 $category = (isset($query[1])) ? $query[1] : null;
55 $object = (isset($query[2])) ? $query[2] : null;
56 $extra = (isset($query[3])) ? $query[3] : null;
57
58 // accept json in request body
59 if(strpos($_SERVER['HTTP_CONTENT_TYPE'], 'application/json') !== false) {
60 $request = file_get_contents('php://input');
61 $requestDecoded = json_decode($request, true);
62
63 // check for valid json
64 if ($action != 'get' && $requestDecoded === null) {
65 http_response_code(400);
66 echo json_encode(array(
67 'type' => 'error',
68 'msg' => 'Request body doesn\'t contain valid json!'
69 ));
70 exit;
71 }
72
73 // add
74 if ($action == 'add') {
75 $_POST['attr'] = $request;
76 }
77
78 // edit
79 if ($action == 'edit') {
80 $_POST['attr'] = json_encode($requestDecoded['attr']);
81 $_POST['items'] = json_encode($requestDecoded['items']);
82 }
83
84 // delete
85 if ($action == 'delete') {
86 $_POST['items'] = $request;
87 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010088 }
89 api_log($_POST);
90
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010091
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010092 $request_incomplete = json_encode(array(
93 'type' => 'error',
94 'msg' => 'Cannot find attributes in post data'
95 ));
96
97 switch ($action) {
98 case "add":
99 if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username'])) {
100 http_response_code(403);
101 echo json_encode(array(
102 'type' => 'error',
103 'msg' => 'API read/write access denied'
104 ));
105 exit();
106 }
107 function process_add_return($return) {
108 $generic_failure = json_encode(array(
109 'type' => 'error',
110 'msg' => 'Cannot add item'
111 ));
112 $generic_success = json_encode(array(
113 'type' => 'success',
114 'msg' => 'Task completed'
115 ));
116 if ($return === false) {
117 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_failure;
118 }
119 else {
120 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_success;
121 }
122 }
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100123 if (!isset($_POST['attr']) && $category != "fido2-registration" && $category != "webauthn-tfa-registration") {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100124 echo $request_incomplete;
125 exit;
126 }
127 else {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100128 if ($category != "fido2-registration" && $category != "webauthn-tfa-registration") {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100129 $attr = (array)json_decode($_POST['attr'], true);
130 }
131 unset($attr['csrf_token']);
132 }
133 // only allow POST requests to POST API endpoints
134 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
135 http_response_code(405);
136 echo json_encode(array(
137 'type' => 'error',
138 'msg' => 'only POST method is allowed'
139 ));
140 exit();
141 }
142
143 switch ($category) {
144 // fido2-registration via POST
145 case "fido2-registration":
146 header('Content-Type: application/json');
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200147 if (isset($_SESSION["mailcow_cc_role"])) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100148 $post = trim(file_get_contents('php://input'));
149 if ($post) {
150 $post = json_decode($post);
151 }
152 $clientDataJSON = base64_decode($post->clientDataJSON);
153 $attestationObject = base64_decode($post->attestationObject);
154 $challenge = $_SESSION['challenge'];
155 try {
156 $data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $challenge, $GLOBALS['FIDO2_UV_FLAG_REGISTER'], $GLOBALS['FIDO2_USER_PRESENT_FLAG']);
157 }
158 catch (Throwable $ex) {
159 $return = new stdClass();
160 $return->success = false;
161 $return->msg = $ex->getMessage();
162 echo json_encode($return);
163 exit;
164 }
165 fido2(array("action" => "register", "registration" => $data));
166 $return = new stdClass();
167 $return->success = true;
168 echo json_encode($return);
169 exit;
170 }
171 else {
172 echo $request_incomplete;
173 exit;
174 }
175 break;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100176 case "webauthn-tfa-registration":
177 if (isset($_SESSION["mailcow_cc_role"])) {
178 // parse post data
179 $post = trim(file_get_contents('php://input'));
180 if ($post) $post = json_decode($post);
181
182 // process registration data from authenticator
183 try {
184 // decode base64 strings
185 $clientDataJSON = base64_decode($post->clientDataJSON);
186 $attestationObject = base64_decode($post->attestationObject);
187
188 // processCreate($clientDataJSON, $attestationObject, $challenge, $requireUserVerification=false, $requireUserPresent=true, $failIfRootMismatch=true)
189 $data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $_SESSION['challenge'], false, true);
190
191 // safe authenticator in mysql `tfa` table
192 $_data['tfa_method'] = $post->tfa_method;
193 $_data['key_id'] = $post->key_id;
194 $_data['confirm_password'] = $post->confirm_password;
195 $_data['registration'] = $data;
196 set_tfa($_data);
197 }
198 catch (Throwable $ex) {
199 // err
200 $return = new stdClass();
201 $return->success = false;
202 $return->msg = $ex->getMessage();
203 echo json_encode($return);
204 exit;
205 }
206
207
208 // send response
209 $return = new stdClass();
210 $return->success = true;
211 echo json_encode($return);
212 exit;
213 }
214 else {
215 // err - request incomplete
216 echo $request_incomplete;
217 exit;
218 }
219 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100220 case "time_limited_alias":
221 process_add_return(mailbox('add', 'time_limited_alias', $attr));
222 break;
223 case "relayhost":
224 process_add_return(relayhost('add', $attr));
225 break;
226 case "transport":
227 process_add_return(transport('add', $attr));
228 break;
229 case "rsetting":
230 process_add_return(rsettings('add', $attr));
231 break;
232 case "mailbox":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100233 switch ($object) {
234 case "template":
235 process_add_return(mailbox('add', 'mailbox_templates', $attr));
236 break;
237 default:
238 process_add_return(mailbox('add', 'mailbox', $attr));
239 break;
240 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100241 break;
242 case "oauth2-client":
243 process_add_return(oauth2('add', 'client', $attr));
244 break;
245 case "domain":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100246 switch ($object) {
247 case "template":
248 process_add_return(mailbox('add', 'domain_templates', $attr));
249 break;
250 default:
251 process_add_return(mailbox('add', 'domain', $attr));
252 break;
253 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100254 break;
255 case "resource":
256 process_add_return(mailbox('add', 'resource', $attr));
257 break;
258 case "alias":
259 process_add_return(mailbox('add', 'alias', $attr));
260 break;
261 case "filter":
262 process_add_return(mailbox('add', 'filter', $attr));
263 break;
264 case "global-filter":
265 process_add_return(mailbox('add', 'global_filter', $attr));
266 break;
267 case "domain-policy":
268 process_add_return(policy('add', 'domain', $attr));
269 break;
270 case "mailbox-policy":
271 process_add_return(policy('add', 'mailbox', $attr));
272 break;
273 case "alias-domain":
274 process_add_return(mailbox('add', 'alias_domain', $attr));
275 break;
276 case "fwdhost":
277 process_add_return(fwdhost('add', $attr));
278 break;
279 case "dkim":
280 process_add_return(dkim('add', $attr));
281 break;
282 case "dkim_duplicate":
283 process_add_return(dkim('duplicate', $attr));
284 break;
285 case "dkim_import":
286 process_add_return(dkim('import', $attr));
287 break;
288 case "domain-admin":
289 process_add_return(domain_admin('add', $attr));
290 break;
291 case "admin":
292 process_add_return(admin('add', $attr));
293 break;
294 case "syncjob":
295 process_add_return(mailbox('add', 'syncjob', $attr));
296 break;
297 case "bcc":
298 process_add_return(bcc('add', $attr));
299 break;
300 case "recipient_map":
301 process_add_return(recipient_map('add', $attr));
302 break;
303 case "tls-policy-map":
304 process_add_return(tls_policy_maps('add', $attr));
305 break;
306 case "app-passwd":
307 process_add_return(app_passwd('add', $attr));
308 break;
309 // return no route found if no case is matched
310 default:
311 http_response_code(404);
312 echo json_encode(array(
313 'type' => 'error',
314 'msg' => 'route not found'
315 ));
316 exit();
317 }
318 break;
319 case "process":
320 // only allow POST requests to process API endpoints
321 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
322 http_response_code(405);
323 echo json_encode(array(
324 'type' => 'error',
325 'msg' => 'only POST method is allowed'
326 ));
327 exit();
328 }
329 switch ($category) {
330 case "fido2-args":
331 header('Content-Type: application/json');
332 $post = trim(file_get_contents('php://input'));
333 if ($post) {
334 $post = json_decode($post);
335 }
336 $clientDataJSON = base64_decode($post->clientDataJSON);
337 $authenticatorData = base64_decode($post->authenticatorData);
338 $signature = base64_decode($post->signature);
339 $id = base64_decode($post->id);
340 $challenge = $_SESSION['challenge'];
341 $process_fido2 = fido2(array("action" => "get_by_b64cid", "cid" => $post->id));
342 if ($process_fido2['pub_key'] === false) {
343 $return = new stdClass();
344 $return->success = false;
345 echo json_encode($return);
346 exit;
347 }
348 try {
349 $WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_fido2['pub_key'], $challenge, null, $GLOBALS['FIDO2_UV_FLAG_LOGIN'], $GLOBALS['FIDO2_USER_PRESENT_FLAG']);
350 }
351 catch (Throwable $ex) {
352 unset($process_fido2);
353 $return = new stdClass();
354 $return->success = false;
355 echo json_encode($return);
356 exit;
357 }
358 $return = new stdClass();
359 $return->success = true;
360 $stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
361 $stmt->execute(array(':username' => $process_fido2['username']));
362 $obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
363 if ($obj_props['superadmin'] === 1) {
364 $_SESSION["mailcow_cc_role"] = "admin";
365 }
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200366 elseif ($obj_props['superadmin'] === 0) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100367 $_SESSION["mailcow_cc_role"] = "domainadmin";
368 }
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200369 else {
370 $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
371 $stmt->execute(array(':username' => $process_fido2['username']));
372 $row = $stmt->fetch(PDO::FETCH_ASSOC);
373 if ($row['username'] == $process_fido2['username']) {
374 $_SESSION["mailcow_cc_role"] = "user";
375 }
376 }
377 if (empty($_SESSION["mailcow_cc_role"])) {
378 session_unset();
379 session_destroy();
380 exit;
381 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100382 $_SESSION["mailcow_cc_username"] = $process_fido2['username'];
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100383 $_SESSION["fido2_cid"] = $process_fido2['cid'];
384 unset($_SESSION["challenge"]);
385 $_SESSION['return'][] = array(
386 'type' => 'success',
387 'log' => array("fido2_login"),
388 'msg' => array('logged_in_as', $process_fido2['username'])
389 );
390 echo json_encode($return);
391 break;
392 }
393 break;
394 case "get":
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200395 function process_get_return($data, $object = true) {
396 if ($object === true) {
397 $ret_str = '{}';
398 }
399 else {
400 $ret_str = '[]';
401 }
402 echo (!isset($data) || empty($data)) ? $ret_str : json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100403 }
404 // only allow GET requests to GET API endpoints
405 if ($_SERVER['REQUEST_METHOD'] != 'GET') {
406 http_response_code(405);
407 echo json_encode(array(
408 'type' => 'error',
409 'msg' => 'only GET method is allowed'
410 ));
411 exit();
412 }
413 switch ($category) {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100414 // fido2
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100415 case "fido2-registration":
416 header('Content-Type: application/json');
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200417 if (isset($_SESSION["mailcow_cc_role"])) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100418 // Exclude existing CredentialIds, if any
419 $excludeCredentialIds = fido2(array("action" => "get_user_cids"));
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100420 $createArgs = $WebAuthn->getCreateArgs($_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], 30, true, $GLOBALS['FIDO2_UV_FLAG_REGISTER'], null, $excludeCredentialIds);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100421 print(json_encode($createArgs));
422 $_SESSION['challenge'] = $WebAuthn->getChallenge();
423 return;
424 }
425 else {
426 return;
427 }
428 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100429 case "fido2-get-args":
430 header('Content-Type: application/json');
431 // Login without username, no ids!
432 // $ids = fido2(array("action" => "get_all_cids"));
433 // if (count($ids) == 0) {
434 // return;
435 // }
436 $ids = NULL;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100437
438 $getArgs = $WebAuthn->getGetArgs($ids, 30, false, false, false, false, $GLOBALS['FIDO2_UV_FLAG_LOGIN']);
439 print(json_encode($getArgs));
440 $_SESSION['challenge'] = $WebAuthn->getChallenge();
441 return;
442 break;
443 // webauthn two factor authentication
444 case "webauthn-tfa-registration":
445 if (isset($_SESSION["mailcow_cc_role"])) {
446 // Exclude existing CredentialIds, if any
447 $stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username AND authmech = :authmech");
448 $stmt->execute(array(
449 ':username' => $_SESSION['mailcow_cc_username'],
450 ':authmech' => 'webauthn'
451 ));
452 $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
453 while($row = array_shift($rows)) {
454 $excludeCredentialIds[] = base64_decode($row['keyHandle']);
455 }
456 // getCreateArgs($userId, $userName, $userDisplayName, $timeout=20, $requireResidentKey=false, $requireUserVerification=false, $crossPlatformAttachment=null, $excludeCredentialIds=array())
457 // cross-platform: true, if type internal is not allowed
458 // false, if only internal is allowed
459 // null, if internal and cross-platform is allowed
460 $createArgs = $WebAuthn->getCreateArgs($_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], 30, false, $GLOBALS['WEBAUTHN_UV_FLAG_REGISTER'], null, $excludeCredentialIds);
461
462 print(json_encode($createArgs));
463 $_SESSION['challenge'] = $WebAuthn->getChallenge();
464 return;
465
466 }
467 else {
468 return;
469 }
470 break;
471 case "webauthn-tfa-get-args":
472 $stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username AND authmech = :authmech");
473 $stmt->execute(array(
474 ':username' => $_SESSION['pending_mailcow_cc_username'],
475 ':authmech' => 'webauthn'
476 ));
477 $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
478 if (count($rows) == 0) {
479 print(json_encode(array(
480 'type' => 'error',
481 'msg' => 'Cannot find matching credentialIds'
482 )));
483 exit;
484 }
485 while($row = array_shift($rows)) {
486 $cids[] = base64_decode($row['keyHandle']);
487 }
488
489 $getArgs = $WebAuthn->getGetArgs($cids, 30, false, false, false, false, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN']);
490 $getArgs->publicKey->extensions = array('appid' => "https://".$getArgs->publicKey->rpId);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100491 print(json_encode($getArgs));
492 $_SESSION['challenge'] = $WebAuthn->getChallenge();
493 return;
494 break;
495 }
496 if (isset($_SESSION['mailcow_cc_role'])) {
497 switch ($category) {
498 case "rspamd":
499 switch ($object) {
500 case "actions":
501 $data = rspamd_actions();
502 if ($data) {
503 echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
504 }
505 else {
506 echo '{}';
507 }
508 break;
509 }
510 break;
511
512 case "domain":
513 switch ($object) {
514 case "all":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100515 $tags = null;
516 if (isset($_GET['tags']) && $_GET['tags'] != '')
517 $tags = explode(',', $_GET['tags']);
518
519 $domains = mailbox('get', 'domains', null, $tags);
520
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100521 if (!empty($domains)) {
522 foreach ($domains as $domain) {
523 if ($details = mailbox('get', 'domain_details', $domain)) {
524 $data[] = $details;
525 }
526 else {
527 continue;
528 }
529 }
530 process_get_return($data);
531 }
532 else {
533 echo '{}';
534 }
535 break;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100536 case "template":
537 switch ($extra){
538 case "all":
539 process_get_return(mailbox('get', 'domain_templates'));
540 break;
541 default:
542 process_get_return(mailbox('get', 'domain_templates', $extra));
543 break;
544 }
545 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100546 default:
547 $data = mailbox('get', 'domain_details', $object);
548 process_get_return($data);
549 break;
550 }
551 break;
552
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200553 case "passwordpolicy":
554 switch ($object) {
555 case "html":
556 $password_complexity_rules = password_complexity('html');
557 if ($password_complexity_rules !== false) {
558 process_get_return($password_complexity_rules);
559 }
560 else {
561 echo '{}';
562 }
563 break;
564 }
565 break;
566
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100567 case "app-passwd":
568 switch ($object) {
569 case "all":
570 if (empty($extra)) {
571 $app_passwds = app_passwd('get');
572 }
573 else {
574 $app_passwds = app_passwd('get', array('username' => $extra));
575 }
576 if (!empty($app_passwds)) {
577 foreach ($app_passwds as $app_passwd) {
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100578 $details = app_passwd('details', $app_passwd['id']);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100579 if ($details !== false) {
580 $data[] = $details;
581 }
582 else {
583 continue;
584 }
585 }
586 process_get_return($data);
587 }
588 else {
589 echo '{}';
590 }
591 break;
592
593 default:
594 $data = app_passwd('details', array('id' => $object['id']));
595 process_get_return($data);
596 break;
597 }
598 break;
599
600 case "mailq":
601 switch ($object) {
602 case "all":
603 $mailq = mailq('get');
604 if (!empty($mailq)) {
605 echo $mailq;
606 }
607 else {
608 echo '[]';
609 }
610 break;
611 }
612 break;
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100613
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100614 case "postcat":
615 switch ($object) {
616 default:
617 $data = mailq('cat', array('qid' => $object));
618 echo $data;
619 break;
620 }
621 break;
622
623 case "global_filters":
624 $global_filters = mailbox('get', 'global_filter_details');
625 switch ($object) {
626 case "all":
627 if (!empty($global_filters)) {
628 process_get_return($global_filters);
629 }
630 else {
631 echo '{}';
632 }
633 break;
634 case "prefilter":
635 if (!empty($global_filters['prefilter'])) {
636 process_get_return($global_filters['prefilter']);
637 }
638 else {
639 echo '{}';
640 }
641 break;
642 case "postfilter":
643 if (!empty($global_filters['postfilter'])) {
644 process_get_return($global_filters['postfilter']);
645 }
646 else {
647 echo '{}';
648 }
649 break;
650 }
651 break;
652
653 case "rl-domain":
654 switch ($object) {
655 case "all":
656 $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
657 if (!empty($domains)) {
658 foreach ($domains as $domain) {
659 if ($details = ratelimit('get', 'domain', $domain)) {
660 $details['domain'] = $domain;
661 $data[] = $details;
662 }
663 else {
664 continue;
665 }
666 }
667 process_get_return($data);
668 }
669 else {
670 echo '{}';
671 }
672 break;
673
674 default:
675 $data = ratelimit('get', 'domain', $object);
676 process_get_return($data);
677 break;
678 }
679 break;
680
681 case "rl-mbox":
682 switch ($object) {
683 case "all":
684 $domains = mailbox('get', 'domains');
685 if (!empty($domains)) {
686 foreach ($domains as $domain) {
687 $mailboxes = mailbox('get', 'mailboxes', $domain);
688 if (!empty($mailboxes)) {
689 foreach ($mailboxes as $mailbox) {
690 if ($details = ratelimit('get', 'mailbox', $mailbox)) {
691 $details['mailbox'] = $mailbox;
692 $data[] = $details;
693 }
694 else {
695 continue;
696 }
697 }
698 }
699 }
700 process_get_return($data);
701 }
702 else {
703 echo '{}';
704 }
705 break;
706
707 default:
708 $data = ratelimit('get', 'mailbox', $object);
709 process_get_return($data);
710 break;
711 }
712 break;
713
714 case "relayhost":
715 switch ($object) {
716 case "all":
717 $relayhosts = relayhost('get');
718 if (!empty($relayhosts)) {
719 foreach ($relayhosts as $relayhost) {
720 if ($details = relayhost('details', $relayhost['id'])) {
721 $data[] = $details;
722 }
723 else {
724 continue;
725 }
726 }
727 process_get_return($data);
728 }
729 else {
730 echo '{}';
731 }
732 break;
733
734 default:
735 $data = relayhost('details', $object);
736 process_get_return($data);
737 break;
738 }
739 break;
740
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200741 case "last-login":
742 if ($object) {
743 // extra == days
744 if (isset($extra) && intval($extra) >= 1) {
745 $data = last_login('get', $object, intval($extra));
746 }
747 else {
748 $data = last_login('get', $object);
749 }
750 process_get_return($data);
751 }
752 break;
753
754 // Todo: move to delete
755 case "reset-last-login":
756 if ($object) {
757 $data = last_login('reset', $object);
758 process_get_return($data);
759 }
760 break;
761
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100762 case "transport":
763 switch ($object) {
764 case "all":
765 $transports = transport('get');
766 if (!empty($transports)) {
767 foreach ($transports as $transport) {
768 if ($details = transport('details', $transport['id'])) {
769 $data[] = $details;
770 }
771 else {
772 continue;
773 }
774 }
775 process_get_return($data);
776 }
777 else {
778 echo '{}';
779 }
780 break;
781
782 default:
783 $data = transport('details', $object);
784 process_get_return($data);
785 break;
786 }
787 break;
788
789 case "rsetting":
790 switch ($object) {
791 case "all":
792 $rsettings = rsettings('get');
793 if (!empty($rsettings)) {
794 foreach ($rsettings as $rsetting) {
795 if ($details = rsettings('details', $rsetting['id'])) {
796 $data[] = $details;
797 }
798 else {
799 continue;
800 }
801 }
802 process_get_return($data);
803 }
804 else {
805 echo '{}';
806 }
807 break;
808
809 default:
810 $data = rsettings('details', $object);
811 process_get_return($data);
812 break;
813 }
814 break;
815
816 case "oauth2-client":
817 switch ($object) {
818 case "all":
819 $clients = oauth2('get', 'clients');
820 if (!empty($clients)) {
821 foreach ($clients as $client) {
822 if ($details = oauth2('details', 'client', $client)) {
823 $data[] = $details;
824 }
825 else {
826 continue;
827 }
828 }
829 process_get_return($data);
830 }
831 else {
832 echo '{}';
833 }
834 break;
835
836 default:
837 $data = oauth2('details', 'client', $object);
838 process_get_return($data);
839 break;
840 }
841 break;
842
843 case "logs":
844 switch ($object) {
845 case "dovecot":
846 // 0 is first record, so empty is fine
847 if (isset($extra)) {
848 $extra = preg_replace('/[^\d\-]/i', '', $extra);
849 $logs = get_logs('dovecot-mailcow', $extra);
850 }
851 else {
852 $logs = get_logs('dovecot-mailcow');
853 }
854 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
855 break;
856 case "ratelimited":
857 // 0 is first record, so empty is fine
858 if (isset($extra)) {
859 $extra = preg_replace('/[^\d\-]/i', '', $extra);
860 $logs = get_logs('ratelimited', $extra);
861 }
862 else {
863 $logs = get_logs('ratelimited');
864 }
865 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
866 break;
867 case "netfilter":
868 // 0 is first record, so empty is fine
869 if (isset($extra)) {
870 $extra = preg_replace('/[^\d\-]/i', '', $extra);
871 $logs = get_logs('netfilter-mailcow', $extra);
872 }
873 else {
874 $logs = get_logs('netfilter-mailcow');
875 }
876 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
877 break;
878 case "postfix":
879 // 0 is first record, so empty is fine
880 if (isset($extra)) {
881 $extra = preg_replace('/[^\d\-]/i', '', $extra);
882 $logs = get_logs('postfix-mailcow', $extra);
883 }
884 else {
885 $logs = get_logs('postfix-mailcow');
886 }
887 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
888 break;
889 case "autodiscover":
890 // 0 is first record, so empty is fine
891 if (isset($extra)) {
892 $extra = preg_replace('/[^\d\-]/i', '', $extra);
893 $logs = get_logs('autodiscover-mailcow', $extra);
894 }
895 else {
896 $logs = get_logs('autodiscover-mailcow');
897 }
898 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
899 break;
900 case "sogo":
901 // 0 is first record, so empty is fine
902 if (isset($extra)) {
903 $extra = preg_replace('/[^\d\-]/i', '', $extra);
904 $logs = get_logs('sogo-mailcow', $extra);
905 }
906 else {
907 $logs = get_logs('sogo-mailcow');
908 }
909 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
910 break;
911 case "ui":
912 // 0 is first record, so empty is fine
913 if (isset($extra)) {
914 $extra = preg_replace('/[^\d\-]/i', '', $extra);
915 $logs = get_logs('mailcow-ui', $extra);
916 }
917 else {
918 $logs = get_logs('mailcow-ui');
919 }
920 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
921 break;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200922 case "sasl":
923 // 0 is first record, so empty is fine
924 if (isset($extra)) {
925 $extra = preg_replace('/[^\d\-]/i', '', $extra);
926 $logs = get_logs('sasl', $extra);
927 }
928 else {
929 $logs = get_logs('sasl');
930 }
931 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
932 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100933 case "watchdog":
934 // 0 is first record, so empty is fine
935 if (isset($extra)) {
936 $extra = preg_replace('/[^\d\-]/i', '', $extra);
937 $logs = get_logs('watchdog-mailcow', $extra);
938 }
939 else {
940 $logs = get_logs('watchdog-mailcow');
941 }
942 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
943 break;
944 case "acme":
945 // 0 is first record, so empty is fine
946 if (isset($extra)) {
947 $extra = preg_replace('/[^\d\-]/i', '', $extra);
948 $logs = get_logs('acme-mailcow', $extra);
949 }
950 else {
951 $logs = get_logs('acme-mailcow');
952 }
953 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
954 break;
955 case "api":
956 // 0 is first record, so empty is fine
957 if (isset($extra)) {
958 $extra = preg_replace('/[^\d\-]/i', '', $extra);
959 $logs = get_logs('api-mailcow', $extra);
960 }
961 else {
962 $logs = get_logs('api-mailcow');
963 }
964 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
965 break;
966 case "rspamd-history":
967 // 0 is first record, so empty is fine
968 if (isset($extra)) {
969 $extra = preg_replace('/[^\d\-]/i', '', $extra);
970 $logs = get_logs('rspamd-history', $extra);
971 }
972 else {
973 $logs = get_logs('rspamd-history');
974 }
975 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
976 break;
977 case "rspamd-stats":
978 $logs = get_logs('rspamd-stats');
979 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
980 break;
981 // return no route found if no case is matched
982 default:
983 http_response_code(404);
984 echo json_encode(array(
985 'type' => 'error',
986 'msg' => 'route not found'
987 ));
988 exit();
989 }
990 break;
991 case "mailbox":
992 switch ($object) {
993 case "all":
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200994 case "reduced":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100995 $tags = null;
996 if (isset($_GET['tags']) && $_GET['tags'] != '')
997 $tags = explode(',', $_GET['tags']);
998
999 if (empty($extra)) $domains = mailbox('get', 'domains');
1000 else $domains = explode(',', $extra);
1001
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001002 if (!empty($domains)) {
1003 foreach ($domains as $domain) {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001004 $mailboxes = mailbox('get', 'mailboxes', $domain, $tags);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001005 if (!empty($mailboxes)) {
1006 foreach ($mailboxes as $mailbox) {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001007 if ($details = mailbox('get', 'mailbox_details', $mailbox, $object)) $data[] = $details;
1008 else continue;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001009 }
1010 }
1011 }
1012 process_get_return($data);
1013 }
1014 else {
1015 echo '{}';
1016 }
1017 break;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001018 case "template":
1019 switch ($extra){
1020 case "all":
1021 process_get_return(mailbox('get', 'mailbox_templates'));
1022 break;
1023 default:
1024 process_get_return(mailbox('get', 'mailbox_templates', $extra));
1025 break;
1026 }
1027 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001028 default:
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001029 $tags = null;
1030 if (isset($_GET['tags']) && $_GET['tags'] != '')
1031 $tags = explode(',', $_GET['tags']);
1032
1033 if ($tags === null) {
1034 $data = mailbox('get', 'mailbox_details', $object);
1035 process_get_return($data);
1036 } else {
1037 $mailboxes = mailbox('get', 'mailboxes', $object, $tags);
1038 if (is_array($mailboxes)) {
1039 foreach ($mailboxes as $mailbox) {
1040 if ($details = mailbox('get', 'mailbox_details', $mailbox))
1041 $data[] = $details;
1042 }
1043 }
1044 process_get_return($data, false);
1045 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001046 break;
1047 }
1048 break;
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +01001049 case "bcc-destination-options":
1050 $domains = mailbox('get', 'domains');
1051 $alias_domains = mailbox('get', 'alias_domains');
1052 $data = array();
1053 if (!empty($domains)) {
1054 foreach ($domains as $domain) {
1055 $data['domains'][] = $domain;
1056 $mailboxes = mailbox('get', 'mailboxes', $domain);
1057 foreach ($mailboxes as $mailbox) {
1058 $data['mailboxes'][$mailbox][] = $mailbox;
1059 $user_alias_details = user_get_alias_details($mailbox);
1060 foreach ($user_alias_details['direct_aliases'] as $k => $v) {
1061 $data['mailboxes'][$mailbox][] = $k;
1062 }
1063 foreach ($user_alias_details['shared_aliases'] as $k => $v) {
1064 $data['mailboxes'][$mailbox][] = $k;
1065 }
1066 }
1067 }
1068 }
1069 if (!empty($alias_domains)) {
1070 foreach ($alias_domains as $alias_domain) {
1071 $data['alias_domains'][] = $alias_domain;
1072 }
1073 }
1074 process_get_return($data);
1075 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001076 case "syncjobs":
1077 switch ($object) {
1078 case "all":
1079 $domains = mailbox('get', 'domains');
1080 if (!empty($domains)) {
1081 foreach ($domains as $domain) {
1082 $mailboxes = mailbox('get', 'mailboxes', $domain);
1083 if (!empty($mailboxes)) {
1084 foreach ($mailboxes as $mailbox) {
1085 $syncjobs = mailbox('get', 'syncjobs', $mailbox);
1086 if (!empty($syncjobs)) {
1087 foreach ($syncjobs as $syncjob) {
1088 if (isset($extra)) {
1089 $details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra));
1090 }
1091 else {
1092 $details = mailbox('get', 'syncjob_details', $syncjob);
1093 }
1094 if ($details) {
1095 $data[] = $details;
1096 }
1097 else {
1098 continue;
1099 }
1100 }
1101 }
1102 }
1103 }
1104 }
1105 process_get_return($data);
1106 }
1107 else {
1108 echo '{}';
1109 }
1110 break;
1111
1112 default:
1113 $syncjobs = mailbox('get', 'syncjobs', $object);
1114 if (!empty($syncjobs)) {
1115 foreach ($syncjobs as $syncjob) {
1116 if (isset($extra)) {
1117 $details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra));
1118 }
1119 else {
1120 $details = mailbox('get', 'syncjob_details', $syncjob);
1121 }
1122 if ($details) {
1123 $data[] = $details;
1124 }
1125 else {
1126 continue;
1127 }
1128 }
1129 }
1130 process_get_return($data);
1131 break;
1132 }
1133 break;
1134 case "active-user-sieve":
1135 if (isset($object)) {
1136 $sieve_filter = mailbox('get', 'active_user_sieve', $object);
1137 if (!empty($sieve_filter)) {
1138 $data[] = $sieve_filter;
1139 }
1140 }
1141 process_get_return($data);
1142 break;
1143 case "filters":
1144 switch ($object) {
1145 case "all":
1146 $domains = mailbox('get', 'domains');
1147 if (!empty($domains)) {
1148 foreach ($domains as $domain) {
1149 $mailboxes = mailbox('get', 'mailboxes', $domain);
1150 if (!empty($mailboxes)) {
1151 foreach ($mailboxes as $mailbox) {
1152 $filters = mailbox('get', 'filters', $mailbox);
1153 if (!empty($filters)) {
1154 foreach ($filters as $filter) {
1155 if ($details = mailbox('get', 'filter_details', $filter)) {
1156 $data[] = $details;
1157 }
1158 else {
1159 continue;
1160 }
1161 }
1162 }
1163 }
1164 }
1165 }
1166 process_get_return($data);
1167 }
1168 else {
1169 echo '{}';
1170 }
1171 break;
1172
1173 default:
1174 $filters = mailbox('get', 'filters', $object);
1175 if (!empty($filters)) {
1176 foreach ($filters as $filter) {
1177 if ($details = mailbox('get', 'filter_details', $filter)) {
1178 $data[] = $details;
1179 }
1180 else {
1181 continue;
1182 }
1183 }
1184 }
1185 process_get_return($data);
1186 break;
1187 }
1188 break;
1189 case "bcc":
1190 switch ($object) {
1191 case "all":
1192 $bcc_items = bcc('get');
1193 if (!empty($bcc_items)) {
1194 foreach ($bcc_items as $bcc_item) {
1195 if ($details = bcc('details', $bcc_item)) {
1196 $data[] = $details;
1197 }
1198 else {
1199 continue;
1200 }
1201 }
1202 }
1203 process_get_return($data);
1204 break;
1205 default:
1206 $data = bcc('details', $object);
1207 if (!empty($data)) {
1208 $data[] = $details;
1209 }
1210 process_get_return($data);
1211 break;
1212 }
1213 break;
1214 case "recipient_map":
1215 switch ($object) {
1216 case "all":
1217 $recipient_map_items = recipient_map('get');
1218 if (!empty($recipient_map_items)) {
1219 foreach ($recipient_map_items as $recipient_map_item) {
1220 if ($details = recipient_map('details', $recipient_map_item)) {
1221 $data[] = $details;
1222 }
1223 else {
1224 continue;
1225 }
1226 }
1227 }
1228 process_get_return($data);
1229 break;
1230 default:
1231 $data = recipient_map('details', $object);
1232 if (!empty($data)) {
1233 $data[] = $details;
1234 }
1235 process_get_return($data);
1236 break;
1237 }
1238 break;
1239 case "tls-policy-map":
1240 switch ($object) {
1241 case "all":
1242 $tls_policy_maps_items = tls_policy_maps('get');
1243 if (!empty($tls_policy_maps_items)) {
1244 foreach ($tls_policy_maps_items as $tls_policy_maps_item) {
1245 if ($details = tls_policy_maps('details', $tls_policy_maps_item)) {
1246 $data[] = $details;
1247 }
1248 else {
1249 continue;
1250 }
1251 }
1252 }
1253 process_get_return($data);
1254 break;
1255 default:
1256 $data = tls_policy_maps('details', $object);
1257 if (!empty($data)) {
1258 $data[] = $details;
1259 }
1260 process_get_return($data);
1261 break;
1262 }
1263 break;
1264 case "policy_wl_mailbox":
1265 switch ($object) {
1266 default:
1267 $data = policy('get', 'mailbox', $object)['whitelist'];
1268 process_get_return($data);
1269 break;
1270 }
1271 break;
1272 case "policy_bl_mailbox":
1273 switch ($object) {
1274 default:
1275 $data = policy('get', 'mailbox', $object)['blacklist'];
1276 process_get_return($data);
1277 break;
1278 }
1279 break;
1280 case "policy_wl_domain":
1281 switch ($object) {
1282 default:
1283 $data = policy('get', 'domain', $object)['whitelist'];
1284 process_get_return($data);
1285 break;
1286 }
1287 break;
1288 case "policy_bl_domain":
1289 switch ($object) {
1290 default:
1291 $data = policy('get', 'domain', $object)['blacklist'];
1292 process_get_return($data);
1293 break;
1294 }
1295 break;
1296 case "time_limited_aliases":
1297 switch ($object) {
1298 default:
1299 $data = mailbox('get', 'time_limited_aliases', $object);
1300 process_get_return($data);
1301 break;
1302 }
1303 break;
1304 case "fail2ban":
1305 switch ($object) {
1306 default:
1307 $data = fail2ban('get');
1308 process_get_return($data);
1309 break;
1310 }
1311 break;
1312 case "resource":
1313 switch ($object) {
1314 case "all":
1315 $domains = mailbox('get', 'domains');
1316 if (!empty($domains)) {
1317 foreach ($domains as $domain) {
1318 $resources = mailbox('get', 'resources', $domain);
1319 if (!empty($resources)) {
1320 foreach ($resources as $resource) {
1321 if ($details = mailbox('get', 'resource_details', $resource)) {
1322 $data[] = $details;
1323 }
1324 else {
1325 continue;
1326 }
1327 }
1328 }
1329 }
1330 process_get_return($data);
1331 }
1332 else {
1333 echo '{}';
1334 }
1335 break;
1336 default:
1337 $data = mailbox('get', 'resource_details', $object);
1338 process_get_return($data);
1339 break;
1340 }
1341 break;
1342 case "fwdhost":
1343 switch ($object) {
1344 case "all":
1345 process_get_return(fwdhost('get'));
1346 break;
1347 default:
1348 process_get_return(fwdhost('details', $object));
1349 break;
1350 }
1351 break;
1352 case "quarantine":
1353 // "all" will not print details
1354 switch ($object) {
1355 case "all":
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001356 process_get_return(quarantine('get'), false);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001357 break;
1358 default:
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001359 process_get_return(quarantine('details', $object), false);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001360 break;
1361 }
1362 break;
1363 case "alias-domain":
1364 switch ($object) {
1365 case "all":
1366 $alias_domains = mailbox('get', 'alias_domains');
1367 if (!empty($alias_domains)) {
1368 foreach ($alias_domains as $alias_domain) {
1369 if ($details = mailbox('get', 'alias_domain_details', $alias_domain)) {
1370 $data[] = $details;
1371 }
1372 else {
1373 continue;
1374 }
1375 }
1376 }
1377 process_get_return($data);
1378 break;
1379 default:
1380 process_get_return(mailbox('get', 'alias_domain_details', $object));
1381 break;
1382 }
1383 break;
1384 case "alias":
1385 switch ($object) {
1386 case "all":
1387 if (empty($extra)) {
1388 $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
1389 }
1390 else {
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001391 $domains = explode(',', $extra);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001392 }
1393 if (!empty($domains)) {
1394 foreach ($domains as $domain) {
1395 $aliases = mailbox('get', 'aliases', $domain);
1396 if (!empty($aliases)) {
1397 foreach ($aliases as $alias) {
1398 if ($details = mailbox('get', 'alias_details', $alias)) {
1399 $data[] = $details;
1400 }
1401 else {
1402 continue;
1403 }
1404 }
1405 }
1406 }
1407 process_get_return($data);
1408 }
1409 else {
1410 echo '{}';
1411 }
1412 break;
1413
1414 default:
1415 process_get_return(mailbox('get', 'alias_details', $object));
1416 break;
1417 }
1418 break;
1419 case "domain-admin":
1420 switch ($object) {
1421 case "all":
1422 $domain_admins = domain_admin('get');
1423 if (!empty($domain_admins)) {
1424 foreach ($domain_admins as $domain_admin) {
1425 if ($details = domain_admin('details', $domain_admin)) {
1426 $data[] = $details;
1427 }
1428 else {
1429 continue;
1430 }
1431 }
1432 process_get_return($data);
1433 }
1434 else {
1435 echo '{}';
1436 }
1437 break;
1438
1439 default:
1440 process_get_return(domain_admin('details', $object));
1441 break;
1442 }
1443 break;
1444 case "admin":
1445 switch ($object) {
1446 case "all":
1447 $admins = admin('get');
1448 if (!empty($admins)) {
1449 foreach ($admins as $admin) {
1450 if ($details = admin('details', $admin)) {
1451 $data[] = $details;
1452 }
1453 else {
1454 continue;
1455 }
1456 }
1457 process_get_return($data);
1458 }
1459 else {
1460 echo '{}';
1461 }
1462 break;
1463
1464 default:
1465 process_get_return(admin('details', $object));
1466 break;
1467 }
1468 break;
1469 case "dkim":
1470 switch ($object) {
1471 default:
1472 $data = dkim('details', $object);
1473 process_get_return($data);
1474 break;
1475 }
1476 break;
1477 case "presets":
1478 switch ($object) {
1479 case "rspamd":
1480 process_get_return(presets('get', 'rspamd'));
1481 break;
1482 case "sieve":
1483 process_get_return(presets('get', 'sieve'));
1484 break;
1485 }
1486 break;
1487 case "status":
1488 if ($_SESSION['mailcow_cc_role'] == "admin") {
1489 switch ($object) {
1490 case "containers":
1491 $containers = (docker('info'));
1492 foreach ($containers as $container => $container_info) {
1493 $container . ' (' . $container_info['Config']['Image'] . ')';
1494 $containerstarttime = ($container_info['State']['StartedAt']);
1495 $containerstate = ($container_info['State']['Status']);
1496 $containerimage = ($container_info['Config']['Image']);
1497 $temp[$container] = array(
1498 'type' => 'info',
1499 'container' => $container,
1500 'state' => $containerstate,
1501 'started_at' => $containerstarttime,
1502 'image' => $containerimage
1503 );
1504 }
1505 echo json_encode($temp, JSON_UNESCAPED_SLASHES);
1506 break;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001507 case "container":
1508 $container_stats = docker('container_stats', $extra);
1509 echo json_encode($container_stats);
1510 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001511 case "vmail":
1512 $exec_fields_vmail = array('cmd' => 'system', 'task' => 'df', 'dir' => '/var/vmail');
1513 $vmail_df = explode(',', json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields_vmail), true));
1514 $temp = array(
1515 'type' => 'info',
1516 'disk' => $vmail_df[0],
1517 'used' => $vmail_df[2],
1518 'total'=> $vmail_df[1],
1519 'used_percent' => $vmail_df[4]
1520 );
1521 echo json_encode($temp, JSON_UNESCAPED_SLASHES);
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001522 break;
1523 case "solr":
1524 $solr_status = solr_status();
1525 $solr_size = ($solr_status['status']['dovecot-fts']['index']['size']);
1526 $solr_documents = ($solr_status['status']['dovecot-fts']['index']['numDocs']);
1527 if (strtolower(getenv('SKIP_SOLR')) != 'n') {
1528 $solr_enabled = false;
1529 }
1530 else {
1531 $solr_enabled = true;
1532 }
1533 echo json_encode(array(
1534 'type' => 'info',
1535 'solr_enabled' => $solr_enabled,
1536 'solr_size' => $solr_size,
1537 'solr_documents' => $solr_documents
1538 ));
1539 break;
1540 case "host":
1541 if (!$extra){
1542 $stats = docker("host_stats");
1543 echo json_encode($stats);
1544 }
1545 else if ($extra == "ip") {
1546 // get public ips
1547 $curl = curl_init();
1548 curl_setopt($curl, CURLOPT_URL, 'http://ipv4.mailcow.email');
1549 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1550 curl_setopt($curl, CURLOPT_POST, 0);
1551 $ipv4 = curl_exec($curl);
1552 curl_setopt($curl, CURLOPT_URL, 'http://ipv6.mailcow.email');
1553 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1554 curl_setopt($curl, CURLOPT_POST, 0);
1555 $ipv6 = curl_exec($curl);
1556 $ips = array(
1557 "ipv4" => $ipv4,
1558 "ipv6" => $ipv6
1559 );
1560 curl_close($curl);
1561 echo json_encode($ips);
1562 }
1563 break;
1564 case "version":
1565 echo json_encode(array(
1566 'version' => $GLOBALS['MAILCOW_GIT_VERSION']
1567 ));
1568 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001569 }
1570 }
1571 break;
1572 break;
1573 // return no route found if no case is matched
1574 default:
1575 http_response_code(404);
1576 echo json_encode(array(
1577 'type' => 'error',
1578 'msg' => 'route not found'
1579 ));
1580 exit();
1581 }
1582 }
1583 break;
1584 case "delete":
1585 if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username']) || !isset($_SESSION["mailcow_cc_username"])) {
1586 http_response_code(403);
1587 echo json_encode(array(
1588 'type' => 'error',
1589 'msg' => 'API read/write access denied'
1590 ));
1591 exit();
1592 }
1593 function process_delete_return($return) {
1594 $generic_failure = json_encode(array(
1595 'type' => 'error',
1596 'msg' => 'Cannot delete item'
1597 ));
1598 $generic_success = json_encode(array(
1599 'type' => 'success',
1600 'msg' => 'Task completed'
1601 ));
1602 if ($return === false) {
1603 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_failure;
1604 }
1605 else {
1606 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_success;
1607 }
1608 }
1609 if (!isset($_POST['items'])) {
1610 echo $request_incomplete;
1611 exit;
1612 }
1613 else {
1614 $items = (array)json_decode($_POST['items'], true);
1615 }
1616 // only allow POST requests to POST API endpoints
1617 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
1618 http_response_code(405);
1619 echo json_encode(array(
1620 'type' => 'error',
1621 'msg' => 'only POST method is allowed'
1622 ));
1623 exit();
1624 }
1625 switch ($category) {
1626 case "alias":
1627 process_delete_return(mailbox('delete', 'alias', array('id' => $items)));
1628 break;
1629 case "oauth2-client":
1630 process_delete_return(oauth2('delete', 'client', array('id' => $items)));
1631 break;
1632 case "app-passwd":
1633 process_delete_return(app_passwd('delete', array('id' => $items)));
1634 break;
1635 case "relayhost":
1636 process_delete_return(relayhost('delete', array('id' => $items)));
1637 break;
1638 case "transport":
1639 process_delete_return(transport('delete', array('id' => $items)));
1640 break;
1641 case "rsetting":
1642 process_delete_return(rsettings('delete', array('id' => $items)));
1643 break;
1644 case "syncjob":
1645 process_delete_return(mailbox('delete', 'syncjob', array('id' => $items)));
1646 break;
1647 case "filter":
1648 process_delete_return(mailbox('delete', 'filter', array('id' => $items)));
1649 break;
1650 case "mailq":
1651 process_delete_return(mailq('delete', array('qid' => $items)));
1652 break;
1653 case "qitem":
1654 process_delete_return(quarantine('delete', array('id' => $items)));
1655 break;
1656 case "bcc":
1657 process_delete_return(bcc('delete', array('id' => $items)));
1658 break;
1659 case "recipient_map":
1660 process_delete_return(recipient_map('delete', array('id' => $items)));
1661 break;
1662 case "tls-policy-map":
1663 process_delete_return(tls_policy_maps('delete', array('id' => $items)));
1664 break;
1665 case "fwdhost":
1666 process_delete_return(fwdhost('delete', array('forwardinghost' => $items)));
1667 break;
1668 case "dkim":
1669 process_delete_return(dkim('delete', array('domains' => $items)));
1670 break;
1671 case "domain":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001672 switch ($object){
1673 case "tag":
1674 process_delete_return(mailbox('delete', 'tags_domain', array('tags' => $items, 'domain' => $extra)));
1675 break;
1676 case "template":
1677 process_delete_return(mailbox('delete', 'domain_templates', array('ids' => $items)));
1678 break;
1679 default:
1680 process_delete_return(mailbox('delete', 'domain', array('domain' => $items)));
1681 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001682 break;
1683 case "alias-domain":
1684 process_delete_return(mailbox('delete', 'alias_domain', array('alias_domain' => $items)));
1685 break;
1686 case "mailbox":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001687 switch ($object){
1688 case "tag":
1689 process_delete_return(mailbox('delete', 'tags_mailbox', array('tags' => $items, 'username' => $extra)));
1690 break;
1691 case "template":
1692 process_delete_return(mailbox('delete', 'mailbox_templates', array('ids' => $items)));
1693 break;
1694 default:
1695 process_delete_return(mailbox('delete', 'mailbox', array('username' => $items)));
1696 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001697 break;
1698 case "resource":
1699 process_delete_return(mailbox('delete', 'resource', array('name' => $items)));
1700 break;
1701 case "mailbox-policy":
1702 process_delete_return(policy('delete', 'mailbox', array('prefid' => $items)));
1703 break;
1704 case "domain-policy":
1705 process_delete_return(policy('delete', 'domain', array('prefid' => $items)));
1706 break;
1707 case "time_limited_alias":
1708 process_delete_return(mailbox('delete', 'time_limited_alias', array('address' => $items)));
1709 break;
1710 case "eas_cache":
1711 process_delete_return(mailbox('delete', 'eas_cache', array('username' => $items)));
1712 break;
1713 case "sogo_profile":
1714 process_delete_return(mailbox('delete', 'sogo_profile', array('username' => $items)));
1715 break;
1716 case "domain-admin":
1717 process_delete_return(domain_admin('delete', array('username' => $items)));
1718 break;
1719 case "admin":
1720 process_delete_return(admin('delete', array('username' => $items)));
1721 break;
1722 case "rlhash":
1723 echo ratelimit('delete', null, implode($items));
1724 break;
1725 // return no route found if no case is matched
1726 default:
1727 http_response_code(404);
1728 echo json_encode(array(
1729 'type' => 'error',
1730 'msg' => 'route not found'
1731 ));
1732 exit();
1733 }
1734 break;
1735 case "edit":
1736 if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username']) || !isset($_SESSION["mailcow_cc_username"])) {
1737 http_response_code(403);
1738 echo json_encode(array(
1739 'type' => 'error',
1740 'msg' => 'API read/write access denied'
1741 ));
1742 exit();
1743 }
1744 function process_edit_return($return) {
1745 $generic_failure = json_encode(array(
1746 'type' => 'error',
1747 'msg' => 'Cannot edit item'
1748 ));
1749 $generic_success = json_encode(array(
1750 'type' => 'success',
1751 'msg' => 'Task completed'
1752 ));
1753 if ($return === false) {
1754 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_failure;
1755 }
1756 else {
1757 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_success;
1758 }
1759 }
1760 if (!isset($_POST['attr'])) {
1761 echo $request_incomplete;
1762 exit;
1763 }
1764 else {
1765 $attr = (array)json_decode($_POST['attr'], true);
1766 unset($attr['csrf_token']);
1767 $items = isset($_POST['items']) ? (array)json_decode($_POST['items'], true) : null;
1768 }
1769 // only allow POST requests to POST API endpoints
1770 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
1771 http_response_code(405);
1772 echo json_encode(array(
1773 'type' => 'error',
1774 'msg' => 'only POST method is allowed'
1775 ));
1776 exit();
1777 }
1778 switch ($category) {
1779 case "bcc":
1780 process_edit_return(bcc('edit', array_merge(array('id' => $items), $attr)));
1781 break;
1782 case "pushover":
1783 process_edit_return(pushover('edit', array_merge(array('username' => $items), $attr)));
1784 break;
1785 case "pushover-test":
1786 process_edit_return(pushover('test', array_merge(array('username' => $items), $attr)));
1787 break;
1788 case "oauth2-client":
1789 process_edit_return(oauth2('edit', 'client', array_merge(array('id' => $items), $attr)));
1790 break;
1791 case "recipient_map":
1792 process_edit_return(recipient_map('edit', array_merge(array('id' => $items), $attr)));
1793 break;
1794 case "app-passwd":
1795 process_edit_return(app_passwd('edit', array_merge(array('id' => $items), $attr)));
1796 break;
1797 case "tls-policy-map":
1798 process_edit_return(tls_policy_maps('edit', array_merge(array('id' => $items), $attr)));
1799 break;
1800 case "alias":
1801 process_edit_return(mailbox('edit', 'alias', array_merge(array('id' => $items), $attr)));
1802 break;
1803 case "rspamd-map":
1804 process_edit_return(rspamd_maps('edit', array_merge(array('map' => $items), $attr)));
1805 break;
1806 case "fido2-fn":
1807 process_edit_return(fido2(array('action' => 'edit_fn', 'fido2_attrs' => $attr)));
1808 break;
1809 case "app_links":
1810 process_edit_return(customize('edit', 'app_links', $attr));
1811 break;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001812 case "passwordpolicy":
1813 process_edit_return(password_complexity('edit', $attr));
1814 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001815 case "relayhost":
1816 process_edit_return(relayhost('edit', array_merge(array('id' => $items), $attr)));
1817 break;
1818 case "transport":
1819 process_edit_return(transport('edit', array_merge(array('id' => $items), $attr)));
1820 break;
1821 case "rsetting":
1822 process_edit_return(rsettings('edit', array_merge(array('id' => $items), $attr)));
1823 break;
1824 case "delimiter_action":
1825 process_edit_return(mailbox('edit', 'delimiter_action', array_merge(array('username' => $items), $attr)));
1826 break;
1827 case "tls_policy":
1828 process_edit_return(mailbox('edit', 'tls_policy', array_merge(array('username' => $items), $attr)));
1829 break;
1830 case "quarantine_notification":
1831 process_edit_return(mailbox('edit', 'quarantine_notification', array_merge(array('username' => $items), $attr)));
1832 break;
1833 case "quarantine_category":
1834 process_edit_return(mailbox('edit', 'quarantine_category', array_merge(array('username' => $items), $attr)));
1835 break;
1836 case "qitem":
1837 process_edit_return(quarantine('edit', array_merge(array('id' => $items), $attr)));
1838 break;
1839 case "quarantine":
1840 process_edit_return(quarantine('edit', $attr));
1841 break;
1842 case "quota_notification":
1843 process_edit_return(quota_notification('edit', $attr));
1844 break;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001845 case "quota_notification_bcc":
1846 process_edit_return(quota_notification_bcc('edit', $attr));
1847 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001848 case "mailq":
1849 process_edit_return(mailq('edit', array_merge(array('qid' => $items), $attr)));
1850 break;
1851 case "time_limited_alias":
1852 process_edit_return(mailbox('edit', 'time_limited_alias', array_merge(array('address' => $items), $attr)));
1853 break;
1854 case "mailbox":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001855 switch ($object) {
1856 case "template":
1857 process_edit_return(mailbox('edit', 'mailbox_templates', array_merge(array('ids' => $items), $attr)));
1858 break;
1859 default:
1860 process_edit_return(mailbox('edit', 'mailbox', array_merge(array('username' => $items), $attr)));
1861 break;
1862 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001863 break;
1864 case "syncjob":
1865 process_edit_return(mailbox('edit', 'syncjob', array_merge(array('id' => $items), $attr)));
1866 break;
1867 case "filter":
1868 process_edit_return(mailbox('edit', 'filter', array_merge(array('id' => $items), $attr)));
1869 break;
1870 case "resource":
1871 process_edit_return(mailbox('edit', 'resource', array_merge(array('name' => $items), $attr)));
1872 break;
1873 case "domain":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001874 switch ($object) {
1875 case "template":
1876 process_edit_return(mailbox('edit', 'domain_templates', array_merge(array('ids' => $items), $attr)));
1877 break;
1878 default:
1879 process_edit_return(mailbox('edit', 'domain', array_merge(array('domain' => $items), $attr)));
1880 break;
1881 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001882 break;
1883 case "rl-domain":
1884 process_edit_return(ratelimit('edit', 'domain', array_merge(array('object' => $items), $attr)));
1885 break;
1886 case "rl-mbox":
1887 process_edit_return(ratelimit('edit', 'mailbox', array_merge(array('object' => $items), $attr)));
1888 break;
1889 case "user-acl":
1890 process_edit_return(acl('edit', 'user', array_merge(array('username' => $items), $attr)));
1891 break;
1892 case "da-acl":
1893 process_edit_return(acl('edit', 'domainadmin', array_merge(array('username' => $items), $attr)));
1894 break;
1895 case "alias-domain":
1896 process_edit_return(mailbox('edit', 'alias_domain', array_merge(array('alias_domain' => $items), $attr)));
1897 break;
1898 case "spam-score":
1899 process_edit_return(mailbox('edit', 'spam_score', array_merge(array('username' => $items), $attr)));
1900 break;
1901 case "domain-admin":
1902 process_edit_return(domain_admin('edit', array_merge(array('username' => $items), $attr)));
1903 break;
1904 case "admin":
1905 process_edit_return(admin('edit', array_merge(array('username' => $items), $attr)));
1906 break;
1907 case "fwdhost":
1908 process_edit_return(fwdhost('edit', array_merge(array('fwdhost' => $items), $attr)));
1909 break;
1910 case "fail2ban":
1911 process_edit_return(fail2ban('edit', array_merge(array('network' => $items), $attr)));
1912 break;
1913 case "ui_texts":
1914 process_edit_return(customize('edit', 'ui_texts', $attr));
1915 break;
1916 case "self":
1917 if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
1918 process_edit_return(domain_admin('edit', $attr));
1919 }
1920 elseif ($_SESSION['mailcow_cc_role'] == "user") {
1921 process_edit_return(edit_user_account($attr));
1922 }
1923 break;
1924 // return no route found if no case is matched
1925 default:
1926 http_response_code(404);
1927 echo json_encode(array(
1928 'type' => 'error',
1929 'msg' => 'route not found'
1930 ));
1931 exit();
1932 }
1933 break;
1934 // return no route found if no case is matched
1935 default:
1936 http_response_code(404);
1937 echo json_encode(array(
1938 'type' => 'error',
1939 'msg' => 'route not found'
1940 ));
1941 exit();
1942 }
1943}
1944if ($_SESSION['mailcow_cc_api'] === true) {
1945 if (isset($_SESSION['mailcow_cc_api']) && $_SESSION['mailcow_cc_api'] === true) {
1946 unset($_SESSION['return']);
1947 }
1948}