blob: b375bc8e49dd26b1a97949ebba1bd3ad4be2b530 [file] [log] [blame]
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001<?php
2/*
3 see /api
4*/
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01005require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01006cors("set_headers");
7header('Content-Type: application/json');
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01008error_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;
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100291 case "sso":
292 switch ($object) {
293 case "domain-admin":
294 $data = domain_admin_sso('issue', $attr);
295 if($data) {
296 echo json_encode($data);
297 exit(0);
298 }
299 process_add_return($data);
300 break;
301 }
302 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100303 case "admin":
304 process_add_return(admin('add', $attr));
305 break;
306 case "syncjob":
307 process_add_return(mailbox('add', 'syncjob', $attr));
308 break;
309 case "bcc":
310 process_add_return(bcc('add', $attr));
311 break;
312 case "recipient_map":
313 process_add_return(recipient_map('add', $attr));
314 break;
315 case "tls-policy-map":
316 process_add_return(tls_policy_maps('add', $attr));
317 break;
318 case "app-passwd":
319 process_add_return(app_passwd('add', $attr));
320 break;
321 // return no route found if no case is matched
322 default:
323 http_response_code(404);
324 echo json_encode(array(
325 'type' => 'error',
326 'msg' => 'route not found'
327 ));
328 exit();
329 }
330 break;
331 case "process":
332 // only allow POST requests to process API endpoints
333 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
334 http_response_code(405);
335 echo json_encode(array(
336 'type' => 'error',
337 'msg' => 'only POST method is allowed'
338 ));
339 exit();
340 }
341 switch ($category) {
342 case "fido2-args":
343 header('Content-Type: application/json');
344 $post = trim(file_get_contents('php://input'));
345 if ($post) {
346 $post = json_decode($post);
347 }
348 $clientDataJSON = base64_decode($post->clientDataJSON);
349 $authenticatorData = base64_decode($post->authenticatorData);
350 $signature = base64_decode($post->signature);
351 $id = base64_decode($post->id);
352 $challenge = $_SESSION['challenge'];
353 $process_fido2 = fido2(array("action" => "get_by_b64cid", "cid" => $post->id));
354 if ($process_fido2['pub_key'] === false) {
355 $return = new stdClass();
356 $return->success = false;
357 echo json_encode($return);
358 exit;
359 }
360 try {
361 $WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_fido2['pub_key'], $challenge, null, $GLOBALS['FIDO2_UV_FLAG_LOGIN'], $GLOBALS['FIDO2_USER_PRESENT_FLAG']);
362 }
363 catch (Throwable $ex) {
364 unset($process_fido2);
365 $return = new stdClass();
366 $return->success = false;
367 echo json_encode($return);
368 exit;
369 }
370 $return = new stdClass();
371 $return->success = true;
372 $stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
373 $stmt->execute(array(':username' => $process_fido2['username']));
374 $obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
375 if ($obj_props['superadmin'] === 1) {
376 $_SESSION["mailcow_cc_role"] = "admin";
377 }
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200378 elseif ($obj_props['superadmin'] === 0) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100379 $_SESSION["mailcow_cc_role"] = "domainadmin";
380 }
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200381 else {
382 $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
383 $stmt->execute(array(':username' => $process_fido2['username']));
384 $row = $stmt->fetch(PDO::FETCH_ASSOC);
385 if ($row['username'] == $process_fido2['username']) {
386 $_SESSION["mailcow_cc_role"] = "user";
387 }
388 }
389 if (empty($_SESSION["mailcow_cc_role"])) {
390 session_unset();
391 session_destroy();
392 exit;
393 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100394 $_SESSION["mailcow_cc_username"] = $process_fido2['username'];
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100395 $_SESSION["fido2_cid"] = $process_fido2['cid'];
396 unset($_SESSION["challenge"]);
397 $_SESSION['return'][] = array(
398 'type' => 'success',
399 'log' => array("fido2_login"),
400 'msg' => array('logged_in_as', $process_fido2['username'])
401 );
402 echo json_encode($return);
403 break;
404 }
405 break;
406 case "get":
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200407 function process_get_return($data, $object = true) {
408 if ($object === true) {
409 $ret_str = '{}';
410 }
411 else {
412 $ret_str = '[]';
413 }
414 echo (!isset($data) || empty($data)) ? $ret_str : json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100415 }
416 // only allow GET requests to GET API endpoints
417 if ($_SERVER['REQUEST_METHOD'] != 'GET') {
418 http_response_code(405);
419 echo json_encode(array(
420 'type' => 'error',
421 'msg' => 'only GET method is allowed'
422 ));
423 exit();
424 }
425 switch ($category) {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100426 // fido2
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100427 case "fido2-registration":
428 header('Content-Type: application/json');
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200429 if (isset($_SESSION["mailcow_cc_role"])) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100430 // Exclude existing CredentialIds, if any
431 $excludeCredentialIds = fido2(array("action" => "get_user_cids"));
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100432 $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 +0100433 print(json_encode($createArgs));
434 $_SESSION['challenge'] = $WebAuthn->getChallenge();
435 return;
436 }
437 else {
438 return;
439 }
440 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100441 case "fido2-get-args":
442 header('Content-Type: application/json');
443 // Login without username, no ids!
444 // $ids = fido2(array("action" => "get_all_cids"));
445 // if (count($ids) == 0) {
446 // return;
447 // }
448 $ids = NULL;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100449
450 $getArgs = $WebAuthn->getGetArgs($ids, 30, false, false, false, false, $GLOBALS['FIDO2_UV_FLAG_LOGIN']);
451 print(json_encode($getArgs));
452 $_SESSION['challenge'] = $WebAuthn->getChallenge();
453 return;
454 break;
455 // webauthn two factor authentication
456 case "webauthn-tfa-registration":
457 if (isset($_SESSION["mailcow_cc_role"])) {
458 // Exclude existing CredentialIds, if any
459 $stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username AND authmech = :authmech");
460 $stmt->execute(array(
461 ':username' => $_SESSION['mailcow_cc_username'],
462 ':authmech' => 'webauthn'
463 ));
464 $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
465 while($row = array_shift($rows)) {
466 $excludeCredentialIds[] = base64_decode($row['keyHandle']);
467 }
468 // getCreateArgs($userId, $userName, $userDisplayName, $timeout=20, $requireResidentKey=false, $requireUserVerification=false, $crossPlatformAttachment=null, $excludeCredentialIds=array())
469 // cross-platform: true, if type internal is not allowed
470 // false, if only internal is allowed
471 // null, if internal and cross-platform is allowed
472 $createArgs = $WebAuthn->getCreateArgs($_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], 30, false, $GLOBALS['WEBAUTHN_UV_FLAG_REGISTER'], null, $excludeCredentialIds);
473
474 print(json_encode($createArgs));
475 $_SESSION['challenge'] = $WebAuthn->getChallenge();
476 return;
477
478 }
479 else {
480 return;
481 }
482 break;
483 case "webauthn-tfa-get-args":
484 $stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username AND authmech = :authmech");
485 $stmt->execute(array(
486 ':username' => $_SESSION['pending_mailcow_cc_username'],
487 ':authmech' => 'webauthn'
488 ));
489 $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
490 if (count($rows) == 0) {
491 print(json_encode(array(
492 'type' => 'error',
493 'msg' => 'Cannot find matching credentialIds'
494 )));
495 exit;
496 }
497 while($row = array_shift($rows)) {
498 $cids[] = base64_decode($row['keyHandle']);
499 }
500
501 $getArgs = $WebAuthn->getGetArgs($cids, 30, false, false, false, false, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN']);
502 $getArgs->publicKey->extensions = array('appid' => "https://".$getArgs->publicKey->rpId);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100503 print(json_encode($getArgs));
504 $_SESSION['challenge'] = $WebAuthn->getChallenge();
505 return;
506 break;
507 }
508 if (isset($_SESSION['mailcow_cc_role'])) {
509 switch ($category) {
510 case "rspamd":
511 switch ($object) {
512 case "actions":
513 $data = rspamd_actions();
514 if ($data) {
515 echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
516 }
517 else {
518 echo '{}';
519 }
520 break;
521 }
522 break;
523
524 case "domain":
525 switch ($object) {
526 case "all":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100527 $tags = null;
528 if (isset($_GET['tags']) && $_GET['tags'] != '')
529 $tags = explode(',', $_GET['tags']);
530
531 $domains = mailbox('get', 'domains', null, $tags);
532
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100533 if (!empty($domains)) {
534 foreach ($domains as $domain) {
535 if ($details = mailbox('get', 'domain_details', $domain)) {
536 $data[] = $details;
537 }
538 else {
539 continue;
540 }
541 }
542 process_get_return($data);
543 }
544 else {
545 echo '{}';
546 }
547 break;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100548 case "template":
549 switch ($extra){
550 case "all":
551 process_get_return(mailbox('get', 'domain_templates'));
552 break;
553 default:
554 process_get_return(mailbox('get', 'domain_templates', $extra));
555 break;
556 }
557 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100558 default:
559 $data = mailbox('get', 'domain_details', $object);
560 process_get_return($data);
561 break;
562 }
563 break;
564
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200565 case "passwordpolicy":
566 switch ($object) {
567 case "html":
568 $password_complexity_rules = password_complexity('html');
569 if ($password_complexity_rules !== false) {
570 process_get_return($password_complexity_rules);
571 }
572 else {
573 echo '{}';
574 }
575 break;
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100576 default:
577 $password_complexity_rules = password_complexity('get');
578 if ($password_complexity_rules !== false) {
579 process_get_return($password_complexity_rules);
580 }
581 else {
582 echo '{}';
583 }
584 break;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200585 }
586 break;
587
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100588 case "app-passwd":
589 switch ($object) {
590 case "all":
591 if (empty($extra)) {
592 $app_passwds = app_passwd('get');
593 }
594 else {
595 $app_passwds = app_passwd('get', array('username' => $extra));
596 }
597 if (!empty($app_passwds)) {
598 foreach ($app_passwds as $app_passwd) {
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100599 $details = app_passwd('details', $app_passwd['id']);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100600 if ($details !== false) {
601 $data[] = $details;
602 }
603 else {
604 continue;
605 }
606 }
607 process_get_return($data);
608 }
609 else {
610 echo '{}';
611 }
612 break;
613
614 default:
615 $data = app_passwd('details', array('id' => $object['id']));
616 process_get_return($data);
617 break;
618 }
619 break;
620
621 case "mailq":
622 switch ($object) {
623 case "all":
624 $mailq = mailq('get');
625 if (!empty($mailq)) {
626 echo $mailq;
627 }
628 else {
629 echo '[]';
630 }
631 break;
632 }
633 break;
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100634
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100635 case "postcat":
636 switch ($object) {
637 default:
638 $data = mailq('cat', array('qid' => $object));
639 echo $data;
640 break;
641 }
642 break;
643
644 case "global_filters":
645 $global_filters = mailbox('get', 'global_filter_details');
646 switch ($object) {
647 case "all":
648 if (!empty($global_filters)) {
649 process_get_return($global_filters);
650 }
651 else {
652 echo '{}';
653 }
654 break;
655 case "prefilter":
656 if (!empty($global_filters['prefilter'])) {
657 process_get_return($global_filters['prefilter']);
658 }
659 else {
660 echo '{}';
661 }
662 break;
663 case "postfilter":
664 if (!empty($global_filters['postfilter'])) {
665 process_get_return($global_filters['postfilter']);
666 }
667 else {
668 echo '{}';
669 }
670 break;
671 }
672 break;
673
674 case "rl-domain":
675 switch ($object) {
676 case "all":
677 $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
678 if (!empty($domains)) {
679 foreach ($domains as $domain) {
680 if ($details = ratelimit('get', 'domain', $domain)) {
681 $details['domain'] = $domain;
682 $data[] = $details;
683 }
684 else {
685 continue;
686 }
687 }
688 process_get_return($data);
689 }
690 else {
691 echo '{}';
692 }
693 break;
694
695 default:
696 $data = ratelimit('get', 'domain', $object);
697 process_get_return($data);
698 break;
699 }
700 break;
701
702 case "rl-mbox":
703 switch ($object) {
704 case "all":
705 $domains = mailbox('get', 'domains');
706 if (!empty($domains)) {
707 foreach ($domains as $domain) {
708 $mailboxes = mailbox('get', 'mailboxes', $domain);
709 if (!empty($mailboxes)) {
710 foreach ($mailboxes as $mailbox) {
711 if ($details = ratelimit('get', 'mailbox', $mailbox)) {
712 $details['mailbox'] = $mailbox;
713 $data[] = $details;
714 }
715 else {
716 continue;
717 }
718 }
719 }
720 }
721 process_get_return($data);
722 }
723 else {
724 echo '{}';
725 }
726 break;
727
728 default:
729 $data = ratelimit('get', 'mailbox', $object);
730 process_get_return($data);
731 break;
732 }
733 break;
734
735 case "relayhost":
736 switch ($object) {
737 case "all":
738 $relayhosts = relayhost('get');
739 if (!empty($relayhosts)) {
740 foreach ($relayhosts as $relayhost) {
741 if ($details = relayhost('details', $relayhost['id'])) {
742 $data[] = $details;
743 }
744 else {
745 continue;
746 }
747 }
748 process_get_return($data);
749 }
750 else {
751 echo '{}';
752 }
753 break;
754
755 default:
756 $data = relayhost('details', $object);
757 process_get_return($data);
758 break;
759 }
760 break;
761
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200762 case "last-login":
763 if ($object) {
764 // extra == days
765 if (isset($extra) && intval($extra) >= 1) {
766 $data = last_login('get', $object, intval($extra));
767 }
768 else {
769 $data = last_login('get', $object);
770 }
771 process_get_return($data);
772 }
773 break;
774
775 // Todo: move to delete
776 case "reset-last-login":
777 if ($object) {
778 $data = last_login('reset', $object);
779 process_get_return($data);
780 }
781 break;
782
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100783 case "transport":
784 switch ($object) {
785 case "all":
786 $transports = transport('get');
787 if (!empty($transports)) {
788 foreach ($transports as $transport) {
789 if ($details = transport('details', $transport['id'])) {
790 $data[] = $details;
791 }
792 else {
793 continue;
794 }
795 }
796 process_get_return($data);
797 }
798 else {
799 echo '{}';
800 }
801 break;
802
803 default:
804 $data = transport('details', $object);
805 process_get_return($data);
806 break;
807 }
808 break;
809
810 case "rsetting":
811 switch ($object) {
812 case "all":
813 $rsettings = rsettings('get');
814 if (!empty($rsettings)) {
815 foreach ($rsettings as $rsetting) {
816 if ($details = rsettings('details', $rsetting['id'])) {
817 $data[] = $details;
818 }
819 else {
820 continue;
821 }
822 }
823 process_get_return($data);
824 }
825 else {
826 echo '{}';
827 }
828 break;
829
830 default:
831 $data = rsettings('details', $object);
832 process_get_return($data);
833 break;
834 }
835 break;
836
837 case "oauth2-client":
838 switch ($object) {
839 case "all":
840 $clients = oauth2('get', 'clients');
841 if (!empty($clients)) {
842 foreach ($clients as $client) {
843 if ($details = oauth2('details', 'client', $client)) {
844 $data[] = $details;
845 }
846 else {
847 continue;
848 }
849 }
850 process_get_return($data);
851 }
852 else {
853 echo '{}';
854 }
855 break;
856
857 default:
858 $data = oauth2('details', 'client', $object);
859 process_get_return($data);
860 break;
861 }
862 break;
863
864 case "logs":
865 switch ($object) {
866 case "dovecot":
867 // 0 is first record, so empty is fine
868 if (isset($extra)) {
869 $extra = preg_replace('/[^\d\-]/i', '', $extra);
870 $logs = get_logs('dovecot-mailcow', $extra);
871 }
872 else {
873 $logs = get_logs('dovecot-mailcow');
874 }
875 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
876 break;
877 case "ratelimited":
878 // 0 is first record, so empty is fine
879 if (isset($extra)) {
880 $extra = preg_replace('/[^\d\-]/i', '', $extra);
881 $logs = get_logs('ratelimited', $extra);
882 }
883 else {
884 $logs = get_logs('ratelimited');
885 }
886 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
887 break;
888 case "netfilter":
889 // 0 is first record, so empty is fine
890 if (isset($extra)) {
891 $extra = preg_replace('/[^\d\-]/i', '', $extra);
892 $logs = get_logs('netfilter-mailcow', $extra);
893 }
894 else {
895 $logs = get_logs('netfilter-mailcow');
896 }
897 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
898 break;
899 case "postfix":
900 // 0 is first record, so empty is fine
901 if (isset($extra)) {
902 $extra = preg_replace('/[^\d\-]/i', '', $extra);
903 $logs = get_logs('postfix-mailcow', $extra);
904 }
905 else {
906 $logs = get_logs('postfix-mailcow');
907 }
908 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
909 break;
910 case "autodiscover":
911 // 0 is first record, so empty is fine
912 if (isset($extra)) {
913 $extra = preg_replace('/[^\d\-]/i', '', $extra);
914 $logs = get_logs('autodiscover-mailcow', $extra);
915 }
916 else {
917 $logs = get_logs('autodiscover-mailcow');
918 }
919 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
920 break;
921 case "sogo":
922 // 0 is first record, so empty is fine
923 if (isset($extra)) {
924 $extra = preg_replace('/[^\d\-]/i', '', $extra);
925 $logs = get_logs('sogo-mailcow', $extra);
926 }
927 else {
928 $logs = get_logs('sogo-mailcow');
929 }
930 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
931 break;
932 case "ui":
933 // 0 is first record, so empty is fine
934 if (isset($extra)) {
935 $extra = preg_replace('/[^\d\-]/i', '', $extra);
936 $logs = get_logs('mailcow-ui', $extra);
937 }
938 else {
939 $logs = get_logs('mailcow-ui');
940 }
941 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
942 break;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200943 case "sasl":
944 // 0 is first record, so empty is fine
945 if (isset($extra)) {
946 $extra = preg_replace('/[^\d\-]/i', '', $extra);
947 $logs = get_logs('sasl', $extra);
948 }
949 else {
950 $logs = get_logs('sasl');
951 }
952 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
953 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100954 case "watchdog":
955 // 0 is first record, so empty is fine
956 if (isset($extra)) {
957 $extra = preg_replace('/[^\d\-]/i', '', $extra);
958 $logs = get_logs('watchdog-mailcow', $extra);
959 }
960 else {
961 $logs = get_logs('watchdog-mailcow');
962 }
963 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
964 break;
965 case "acme":
966 // 0 is first record, so empty is fine
967 if (isset($extra)) {
968 $extra = preg_replace('/[^\d\-]/i', '', $extra);
969 $logs = get_logs('acme-mailcow', $extra);
970 }
971 else {
972 $logs = get_logs('acme-mailcow');
973 }
974 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
975 break;
976 case "api":
977 // 0 is first record, so empty is fine
978 if (isset($extra)) {
979 $extra = preg_replace('/[^\d\-]/i', '', $extra);
980 $logs = get_logs('api-mailcow', $extra);
981 }
982 else {
983 $logs = get_logs('api-mailcow');
984 }
985 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
986 break;
987 case "rspamd-history":
988 // 0 is first record, so empty is fine
989 if (isset($extra)) {
990 $extra = preg_replace('/[^\d\-]/i', '', $extra);
991 $logs = get_logs('rspamd-history', $extra);
992 }
993 else {
994 $logs = get_logs('rspamd-history');
995 }
996 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
997 break;
998 case "rspamd-stats":
999 $logs = get_logs('rspamd-stats');
1000 echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
1001 break;
1002 // return no route found if no case is matched
1003 default:
1004 http_response_code(404);
1005 echo json_encode(array(
1006 'type' => 'error',
1007 'msg' => 'route not found'
1008 ));
1009 exit();
1010 }
1011 break;
1012 case "mailbox":
1013 switch ($object) {
1014 case "all":
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001015 case "reduced":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001016 $tags = null;
1017 if (isset($_GET['tags']) && $_GET['tags'] != '')
1018 $tags = explode(',', $_GET['tags']);
1019
1020 if (empty($extra)) $domains = mailbox('get', 'domains');
1021 else $domains = explode(',', $extra);
1022
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001023 if (!empty($domains)) {
1024 foreach ($domains as $domain) {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001025 $mailboxes = mailbox('get', 'mailboxes', $domain, $tags);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001026 if (!empty($mailboxes)) {
1027 foreach ($mailboxes as $mailbox) {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001028 if ($details = mailbox('get', 'mailbox_details', $mailbox, $object)) $data[] = $details;
1029 else continue;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001030 }
1031 }
1032 }
1033 process_get_return($data);
1034 }
1035 else {
1036 echo '{}';
1037 }
1038 break;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001039 case "template":
1040 switch ($extra){
1041 case "all":
1042 process_get_return(mailbox('get', 'mailbox_templates'));
1043 break;
1044 default:
1045 process_get_return(mailbox('get', 'mailbox_templates', $extra));
1046 break;
1047 }
1048 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001049 default:
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001050 $tags = null;
1051 if (isset($_GET['tags']) && $_GET['tags'] != '')
1052 $tags = explode(',', $_GET['tags']);
1053
1054 if ($tags === null) {
1055 $data = mailbox('get', 'mailbox_details', $object);
1056 process_get_return($data);
1057 } else {
1058 $mailboxes = mailbox('get', 'mailboxes', $object, $tags);
1059 if (is_array($mailboxes)) {
1060 foreach ($mailboxes as $mailbox) {
1061 if ($details = mailbox('get', 'mailbox_details', $mailbox))
1062 $data[] = $details;
1063 }
1064 }
1065 process_get_return($data, false);
1066 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001067 break;
1068 }
1069 break;
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +01001070 case "bcc-destination-options":
1071 $domains = mailbox('get', 'domains');
1072 $alias_domains = mailbox('get', 'alias_domains');
1073 $data = array();
1074 if (!empty($domains)) {
1075 foreach ($domains as $domain) {
1076 $data['domains'][] = $domain;
1077 $mailboxes = mailbox('get', 'mailboxes', $domain);
1078 foreach ($mailboxes as $mailbox) {
1079 $data['mailboxes'][$mailbox][] = $mailbox;
1080 $user_alias_details = user_get_alias_details($mailbox);
1081 foreach ($user_alias_details['direct_aliases'] as $k => $v) {
1082 $data['mailboxes'][$mailbox][] = $k;
1083 }
1084 foreach ($user_alias_details['shared_aliases'] as $k => $v) {
1085 $data['mailboxes'][$mailbox][] = $k;
1086 }
1087 }
1088 }
1089 }
1090 if (!empty($alias_domains)) {
1091 foreach ($alias_domains as $alias_domain) {
1092 $data['alias_domains'][] = $alias_domain;
1093 }
1094 }
1095 process_get_return($data);
1096 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001097 case "syncjobs":
1098 switch ($object) {
1099 case "all":
1100 $domains = mailbox('get', 'domains');
1101 if (!empty($domains)) {
1102 foreach ($domains as $domain) {
1103 $mailboxes = mailbox('get', 'mailboxes', $domain);
1104 if (!empty($mailboxes)) {
1105 foreach ($mailboxes as $mailbox) {
1106 $syncjobs = mailbox('get', 'syncjobs', $mailbox);
1107 if (!empty($syncjobs)) {
1108 foreach ($syncjobs as $syncjob) {
1109 if (isset($extra)) {
1110 $details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra));
1111 }
1112 else {
1113 $details = mailbox('get', 'syncjob_details', $syncjob);
1114 }
1115 if ($details) {
1116 $data[] = $details;
1117 }
1118 else {
1119 continue;
1120 }
1121 }
1122 }
1123 }
1124 }
1125 }
1126 process_get_return($data);
1127 }
1128 else {
1129 echo '{}';
1130 }
1131 break;
1132
1133 default:
1134 $syncjobs = mailbox('get', 'syncjobs', $object);
1135 if (!empty($syncjobs)) {
1136 foreach ($syncjobs as $syncjob) {
1137 if (isset($extra)) {
1138 $details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra));
1139 }
1140 else {
1141 $details = mailbox('get', 'syncjob_details', $syncjob);
1142 }
1143 if ($details) {
1144 $data[] = $details;
1145 }
1146 else {
1147 continue;
1148 }
1149 }
1150 }
1151 process_get_return($data);
1152 break;
1153 }
1154 break;
1155 case "active-user-sieve":
1156 if (isset($object)) {
1157 $sieve_filter = mailbox('get', 'active_user_sieve', $object);
1158 if (!empty($sieve_filter)) {
1159 $data[] = $sieve_filter;
1160 }
1161 }
1162 process_get_return($data);
1163 break;
1164 case "filters":
1165 switch ($object) {
1166 case "all":
1167 $domains = mailbox('get', 'domains');
1168 if (!empty($domains)) {
1169 foreach ($domains as $domain) {
1170 $mailboxes = mailbox('get', 'mailboxes', $domain);
1171 if (!empty($mailboxes)) {
1172 foreach ($mailboxes as $mailbox) {
1173 $filters = mailbox('get', 'filters', $mailbox);
1174 if (!empty($filters)) {
1175 foreach ($filters as $filter) {
1176 if ($details = mailbox('get', 'filter_details', $filter)) {
1177 $data[] = $details;
1178 }
1179 else {
1180 continue;
1181 }
1182 }
1183 }
1184 }
1185 }
1186 }
1187 process_get_return($data);
1188 }
1189 else {
1190 echo '{}';
1191 }
1192 break;
1193
1194 default:
1195 $filters = mailbox('get', 'filters', $object);
1196 if (!empty($filters)) {
1197 foreach ($filters as $filter) {
1198 if ($details = mailbox('get', 'filter_details', $filter)) {
1199 $data[] = $details;
1200 }
1201 else {
1202 continue;
1203 }
1204 }
1205 }
1206 process_get_return($data);
1207 break;
1208 }
1209 break;
1210 case "bcc":
1211 switch ($object) {
1212 case "all":
1213 $bcc_items = bcc('get');
1214 if (!empty($bcc_items)) {
1215 foreach ($bcc_items as $bcc_item) {
1216 if ($details = bcc('details', $bcc_item)) {
1217 $data[] = $details;
1218 }
1219 else {
1220 continue;
1221 }
1222 }
1223 }
1224 process_get_return($data);
1225 break;
1226 default:
1227 $data = bcc('details', $object);
1228 if (!empty($data)) {
1229 $data[] = $details;
1230 }
1231 process_get_return($data);
1232 break;
1233 }
1234 break;
1235 case "recipient_map":
1236 switch ($object) {
1237 case "all":
1238 $recipient_map_items = recipient_map('get');
1239 if (!empty($recipient_map_items)) {
1240 foreach ($recipient_map_items as $recipient_map_item) {
1241 if ($details = recipient_map('details', $recipient_map_item)) {
1242 $data[] = $details;
1243 }
1244 else {
1245 continue;
1246 }
1247 }
1248 }
1249 process_get_return($data);
1250 break;
1251 default:
1252 $data = recipient_map('details', $object);
1253 if (!empty($data)) {
1254 $data[] = $details;
1255 }
1256 process_get_return($data);
1257 break;
1258 }
1259 break;
1260 case "tls-policy-map":
1261 switch ($object) {
1262 case "all":
1263 $tls_policy_maps_items = tls_policy_maps('get');
1264 if (!empty($tls_policy_maps_items)) {
1265 foreach ($tls_policy_maps_items as $tls_policy_maps_item) {
1266 if ($details = tls_policy_maps('details', $tls_policy_maps_item)) {
1267 $data[] = $details;
1268 }
1269 else {
1270 continue;
1271 }
1272 }
1273 }
1274 process_get_return($data);
1275 break;
1276 default:
1277 $data = tls_policy_maps('details', $object);
1278 if (!empty($data)) {
1279 $data[] = $details;
1280 }
1281 process_get_return($data);
1282 break;
1283 }
1284 break;
1285 case "policy_wl_mailbox":
1286 switch ($object) {
1287 default:
1288 $data = policy('get', 'mailbox', $object)['whitelist'];
1289 process_get_return($data);
1290 break;
1291 }
1292 break;
1293 case "policy_bl_mailbox":
1294 switch ($object) {
1295 default:
1296 $data = policy('get', 'mailbox', $object)['blacklist'];
1297 process_get_return($data);
1298 break;
1299 }
1300 break;
1301 case "policy_wl_domain":
1302 switch ($object) {
1303 default:
1304 $data = policy('get', 'domain', $object)['whitelist'];
1305 process_get_return($data);
1306 break;
1307 }
1308 break;
1309 case "policy_bl_domain":
1310 switch ($object) {
1311 default:
1312 $data = policy('get', 'domain', $object)['blacklist'];
1313 process_get_return($data);
1314 break;
1315 }
1316 break;
1317 case "time_limited_aliases":
1318 switch ($object) {
1319 default:
1320 $data = mailbox('get', 'time_limited_aliases', $object);
1321 process_get_return($data);
1322 break;
1323 }
1324 break;
1325 case "fail2ban":
1326 switch ($object) {
1327 default:
1328 $data = fail2ban('get');
1329 process_get_return($data);
1330 break;
1331 }
1332 break;
1333 case "resource":
1334 switch ($object) {
1335 case "all":
1336 $domains = mailbox('get', 'domains');
1337 if (!empty($domains)) {
1338 foreach ($domains as $domain) {
1339 $resources = mailbox('get', 'resources', $domain);
1340 if (!empty($resources)) {
1341 foreach ($resources as $resource) {
1342 if ($details = mailbox('get', 'resource_details', $resource)) {
1343 $data[] = $details;
1344 }
1345 else {
1346 continue;
1347 }
1348 }
1349 }
1350 }
1351 process_get_return($data);
1352 }
1353 else {
1354 echo '{}';
1355 }
1356 break;
1357 default:
1358 $data = mailbox('get', 'resource_details', $object);
1359 process_get_return($data);
1360 break;
1361 }
1362 break;
1363 case "fwdhost":
1364 switch ($object) {
1365 case "all":
1366 process_get_return(fwdhost('get'));
1367 break;
1368 default:
1369 process_get_return(fwdhost('details', $object));
1370 break;
1371 }
1372 break;
1373 case "quarantine":
1374 // "all" will not print details
1375 switch ($object) {
1376 case "all":
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001377 process_get_return(quarantine('get'), false);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001378 break;
1379 default:
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001380 process_get_return(quarantine('details', $object), false);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001381 break;
1382 }
1383 break;
1384 case "alias-domain":
1385 switch ($object) {
1386 case "all":
1387 $alias_domains = mailbox('get', 'alias_domains');
1388 if (!empty($alias_domains)) {
1389 foreach ($alias_domains as $alias_domain) {
1390 if ($details = mailbox('get', 'alias_domain_details', $alias_domain)) {
1391 $data[] = $details;
1392 }
1393 else {
1394 continue;
1395 }
1396 }
1397 }
1398 process_get_return($data);
1399 break;
1400 default:
1401 process_get_return(mailbox('get', 'alias_domain_details', $object));
1402 break;
1403 }
1404 break;
1405 case "alias":
1406 switch ($object) {
1407 case "all":
1408 if (empty($extra)) {
1409 $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
1410 }
1411 else {
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001412 $domains = explode(',', $extra);
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001413 }
1414 if (!empty($domains)) {
1415 foreach ($domains as $domain) {
1416 $aliases = mailbox('get', 'aliases', $domain);
1417 if (!empty($aliases)) {
1418 foreach ($aliases as $alias) {
1419 if ($details = mailbox('get', 'alias_details', $alias)) {
1420 $data[] = $details;
1421 }
1422 else {
1423 continue;
1424 }
1425 }
1426 }
1427 }
1428 process_get_return($data);
1429 }
1430 else {
1431 echo '{}';
1432 }
1433 break;
1434
1435 default:
1436 process_get_return(mailbox('get', 'alias_details', $object));
1437 break;
1438 }
1439 break;
1440 case "domain-admin":
1441 switch ($object) {
1442 case "all":
1443 $domain_admins = domain_admin('get');
1444 if (!empty($domain_admins)) {
1445 foreach ($domain_admins as $domain_admin) {
1446 if ($details = domain_admin('details', $domain_admin)) {
1447 $data[] = $details;
1448 }
1449 else {
1450 continue;
1451 }
1452 }
1453 process_get_return($data);
1454 }
1455 else {
1456 echo '{}';
1457 }
1458 break;
1459
1460 default:
1461 process_get_return(domain_admin('details', $object));
1462 break;
1463 }
1464 break;
1465 case "admin":
1466 switch ($object) {
1467 case "all":
1468 $admins = admin('get');
1469 if (!empty($admins)) {
1470 foreach ($admins as $admin) {
1471 if ($details = admin('details', $admin)) {
1472 $data[] = $details;
1473 }
1474 else {
1475 continue;
1476 }
1477 }
1478 process_get_return($data);
1479 }
1480 else {
1481 echo '{}';
1482 }
1483 break;
1484
1485 default:
1486 process_get_return(admin('details', $object));
1487 break;
1488 }
1489 break;
1490 case "dkim":
1491 switch ($object) {
1492 default:
1493 $data = dkim('details', $object);
1494 process_get_return($data);
1495 break;
1496 }
1497 break;
1498 case "presets":
1499 switch ($object) {
1500 case "rspamd":
1501 process_get_return(presets('get', 'rspamd'));
1502 break;
1503 case "sieve":
1504 process_get_return(presets('get', 'sieve'));
1505 break;
1506 }
1507 break;
1508 case "status":
1509 if ($_SESSION['mailcow_cc_role'] == "admin") {
1510 switch ($object) {
1511 case "containers":
1512 $containers = (docker('info'));
1513 foreach ($containers as $container => $container_info) {
1514 $container . ' (' . $container_info['Config']['Image'] . ')';
1515 $containerstarttime = ($container_info['State']['StartedAt']);
1516 $containerstate = ($container_info['State']['Status']);
1517 $containerimage = ($container_info['Config']['Image']);
1518 $temp[$container] = array(
1519 'type' => 'info',
1520 'container' => $container,
1521 'state' => $containerstate,
1522 'started_at' => $containerstarttime,
1523 'image' => $containerimage
1524 );
1525 }
1526 echo json_encode($temp, JSON_UNESCAPED_SLASHES);
1527 break;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001528 case "container":
1529 $container_stats = docker('container_stats', $extra);
1530 echo json_encode($container_stats);
1531 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001532 case "vmail":
1533 $exec_fields_vmail = array('cmd' => 'system', 'task' => 'df', 'dir' => '/var/vmail');
1534 $vmail_df = explode(',', json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields_vmail), true));
1535 $temp = array(
1536 'type' => 'info',
1537 'disk' => $vmail_df[0],
1538 'used' => $vmail_df[2],
1539 'total'=> $vmail_df[1],
1540 'used_percent' => $vmail_df[4]
1541 );
1542 echo json_encode($temp, JSON_UNESCAPED_SLASHES);
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001543 break;
1544 case "solr":
1545 $solr_status = solr_status();
1546 $solr_size = ($solr_status['status']['dovecot-fts']['index']['size']);
1547 $solr_documents = ($solr_status['status']['dovecot-fts']['index']['numDocs']);
1548 if (strtolower(getenv('SKIP_SOLR')) != 'n') {
1549 $solr_enabled = false;
1550 }
1551 else {
1552 $solr_enabled = true;
1553 }
1554 echo json_encode(array(
1555 'type' => 'info',
1556 'solr_enabled' => $solr_enabled,
1557 'solr_size' => $solr_size,
1558 'solr_documents' => $solr_documents
1559 ));
1560 break;
1561 case "host":
1562 if (!$extra){
1563 $stats = docker("host_stats");
1564 echo json_encode($stats);
1565 }
1566 else if ($extra == "ip") {
1567 // get public ips
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001568
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001569 $curl = curl_init();
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001570 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1571 curl_setopt($curl, CURLOPT_POST, 0);
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001572 curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
1573 curl_setopt($curl, CURLOPT_TIMEOUT, 15);
1574 curl_setopt($curl, CURLOPT_URL, 'http://ipv4.mailcow.email');
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001575 $ipv4 = curl_exec($curl);
1576 curl_setopt($curl, CURLOPT_URL, 'http://ipv6.mailcow.email');
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001577 $ipv6 = curl_exec($curl);
1578 $ips = array(
1579 "ipv4" => $ipv4,
1580 "ipv6" => $ipv6
1581 );
1582 curl_close($curl);
1583 echo json_encode($ips);
1584 }
1585 break;
1586 case "version":
1587 echo json_encode(array(
1588 'version' => $GLOBALS['MAILCOW_GIT_VERSION']
1589 ));
1590 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001591 }
1592 }
1593 break;
1594 break;
1595 // return no route found if no case is matched
1596 default:
1597 http_response_code(404);
1598 echo json_encode(array(
1599 'type' => 'error',
1600 'msg' => 'route not found'
1601 ));
1602 exit();
1603 }
1604 }
1605 break;
1606 case "delete":
1607 if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username']) || !isset($_SESSION["mailcow_cc_username"])) {
1608 http_response_code(403);
1609 echo json_encode(array(
1610 'type' => 'error',
1611 'msg' => 'API read/write access denied'
1612 ));
1613 exit();
1614 }
1615 function process_delete_return($return) {
1616 $generic_failure = json_encode(array(
1617 'type' => 'error',
1618 'msg' => 'Cannot delete item'
1619 ));
1620 $generic_success = json_encode(array(
1621 'type' => 'success',
1622 'msg' => 'Task completed'
1623 ));
1624 if ($return === false) {
1625 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_failure;
1626 }
1627 else {
1628 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_success;
1629 }
1630 }
1631 if (!isset($_POST['items'])) {
1632 echo $request_incomplete;
1633 exit;
1634 }
1635 else {
1636 $items = (array)json_decode($_POST['items'], true);
1637 }
1638 // only allow POST requests to POST API endpoints
1639 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
1640 http_response_code(405);
1641 echo json_encode(array(
1642 'type' => 'error',
1643 'msg' => 'only POST method is allowed'
1644 ));
1645 exit();
1646 }
1647 switch ($category) {
1648 case "alias":
1649 process_delete_return(mailbox('delete', 'alias', array('id' => $items)));
1650 break;
1651 case "oauth2-client":
1652 process_delete_return(oauth2('delete', 'client', array('id' => $items)));
1653 break;
1654 case "app-passwd":
1655 process_delete_return(app_passwd('delete', array('id' => $items)));
1656 break;
1657 case "relayhost":
1658 process_delete_return(relayhost('delete', array('id' => $items)));
1659 break;
1660 case "transport":
1661 process_delete_return(transport('delete', array('id' => $items)));
1662 break;
1663 case "rsetting":
1664 process_delete_return(rsettings('delete', array('id' => $items)));
1665 break;
1666 case "syncjob":
1667 process_delete_return(mailbox('delete', 'syncjob', array('id' => $items)));
1668 break;
1669 case "filter":
1670 process_delete_return(mailbox('delete', 'filter', array('id' => $items)));
1671 break;
1672 case "mailq":
1673 process_delete_return(mailq('delete', array('qid' => $items)));
1674 break;
1675 case "qitem":
1676 process_delete_return(quarantine('delete', array('id' => $items)));
1677 break;
1678 case "bcc":
1679 process_delete_return(bcc('delete', array('id' => $items)));
1680 break;
1681 case "recipient_map":
1682 process_delete_return(recipient_map('delete', array('id' => $items)));
1683 break;
1684 case "tls-policy-map":
1685 process_delete_return(tls_policy_maps('delete', array('id' => $items)));
1686 break;
1687 case "fwdhost":
1688 process_delete_return(fwdhost('delete', array('forwardinghost' => $items)));
1689 break;
1690 case "dkim":
1691 process_delete_return(dkim('delete', array('domains' => $items)));
1692 break;
1693 case "domain":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001694 switch ($object){
1695 case "tag":
1696 process_delete_return(mailbox('delete', 'tags_domain', array('tags' => $items, 'domain' => $extra)));
1697 break;
1698 case "template":
1699 process_delete_return(mailbox('delete', 'domain_templates', array('ids' => $items)));
1700 break;
1701 default:
1702 process_delete_return(mailbox('delete', 'domain', array('domain' => $items)));
1703 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001704 break;
1705 case "alias-domain":
1706 process_delete_return(mailbox('delete', 'alias_domain', array('alias_domain' => $items)));
1707 break;
1708 case "mailbox":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001709 switch ($object){
1710 case "tag":
1711 process_delete_return(mailbox('delete', 'tags_mailbox', array('tags' => $items, 'username' => $extra)));
1712 break;
1713 case "template":
1714 process_delete_return(mailbox('delete', 'mailbox_templates', array('ids' => $items)));
1715 break;
1716 default:
1717 process_delete_return(mailbox('delete', 'mailbox', array('username' => $items)));
1718 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001719 break;
1720 case "resource":
1721 process_delete_return(mailbox('delete', 'resource', array('name' => $items)));
1722 break;
1723 case "mailbox-policy":
1724 process_delete_return(policy('delete', 'mailbox', array('prefid' => $items)));
1725 break;
1726 case "domain-policy":
1727 process_delete_return(policy('delete', 'domain', array('prefid' => $items)));
1728 break;
1729 case "time_limited_alias":
1730 process_delete_return(mailbox('delete', 'time_limited_alias', array('address' => $items)));
1731 break;
1732 case "eas_cache":
1733 process_delete_return(mailbox('delete', 'eas_cache', array('username' => $items)));
1734 break;
1735 case "sogo_profile":
1736 process_delete_return(mailbox('delete', 'sogo_profile', array('username' => $items)));
1737 break;
1738 case "domain-admin":
1739 process_delete_return(domain_admin('delete', array('username' => $items)));
1740 break;
1741 case "admin":
1742 process_delete_return(admin('delete', array('username' => $items)));
1743 break;
1744 case "rlhash":
1745 echo ratelimit('delete', null, implode($items));
1746 break;
1747 // return no route found if no case is matched
1748 default:
1749 http_response_code(404);
1750 echo json_encode(array(
1751 'type' => 'error',
1752 'msg' => 'route not found'
1753 ));
1754 exit();
1755 }
1756 break;
1757 case "edit":
1758 if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username']) || !isset($_SESSION["mailcow_cc_username"])) {
1759 http_response_code(403);
1760 echo json_encode(array(
1761 'type' => 'error',
1762 'msg' => 'API read/write access denied'
1763 ));
1764 exit();
1765 }
1766 function process_edit_return($return) {
1767 $generic_failure = json_encode(array(
1768 'type' => 'error',
1769 'msg' => 'Cannot edit item'
1770 ));
1771 $generic_success = json_encode(array(
1772 'type' => 'success',
1773 'msg' => 'Task completed'
1774 ));
1775 if ($return === false) {
1776 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_failure;
1777 }
1778 else {
1779 echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_success;
1780 }
1781 }
1782 if (!isset($_POST['attr'])) {
1783 echo $request_incomplete;
1784 exit;
1785 }
1786 else {
1787 $attr = (array)json_decode($_POST['attr'], true);
1788 unset($attr['csrf_token']);
1789 $items = isset($_POST['items']) ? (array)json_decode($_POST['items'], true) : null;
1790 }
1791 // only allow POST requests to POST API endpoints
1792 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
1793 http_response_code(405);
1794 echo json_encode(array(
1795 'type' => 'error',
1796 'msg' => 'only POST method is allowed'
1797 ));
1798 exit();
1799 }
1800 switch ($category) {
1801 case "bcc":
1802 process_edit_return(bcc('edit', array_merge(array('id' => $items), $attr)));
1803 break;
1804 case "pushover":
1805 process_edit_return(pushover('edit', array_merge(array('username' => $items), $attr)));
1806 break;
1807 case "pushover-test":
1808 process_edit_return(pushover('test', array_merge(array('username' => $items), $attr)));
1809 break;
1810 case "oauth2-client":
1811 process_edit_return(oauth2('edit', 'client', array_merge(array('id' => $items), $attr)));
1812 break;
1813 case "recipient_map":
1814 process_edit_return(recipient_map('edit', array_merge(array('id' => $items), $attr)));
1815 break;
1816 case "app-passwd":
1817 process_edit_return(app_passwd('edit', array_merge(array('id' => $items), $attr)));
1818 break;
1819 case "tls-policy-map":
1820 process_edit_return(tls_policy_maps('edit', array_merge(array('id' => $items), $attr)));
1821 break;
1822 case "alias":
1823 process_edit_return(mailbox('edit', 'alias', array_merge(array('id' => $items), $attr)));
1824 break;
1825 case "rspamd-map":
1826 process_edit_return(rspamd_maps('edit', array_merge(array('map' => $items), $attr)));
1827 break;
1828 case "fido2-fn":
1829 process_edit_return(fido2(array('action' => 'edit_fn', 'fido2_attrs' => $attr)));
1830 break;
1831 case "app_links":
1832 process_edit_return(customize('edit', 'app_links', $attr));
1833 break;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001834 case "passwordpolicy":
1835 process_edit_return(password_complexity('edit', $attr));
1836 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001837 case "relayhost":
1838 process_edit_return(relayhost('edit', array_merge(array('id' => $items), $attr)));
1839 break;
1840 case "transport":
1841 process_edit_return(transport('edit', array_merge(array('id' => $items), $attr)));
1842 break;
1843 case "rsetting":
1844 process_edit_return(rsettings('edit', array_merge(array('id' => $items), $attr)));
1845 break;
1846 case "delimiter_action":
1847 process_edit_return(mailbox('edit', 'delimiter_action', array_merge(array('username' => $items), $attr)));
1848 break;
1849 case "tls_policy":
1850 process_edit_return(mailbox('edit', 'tls_policy', array_merge(array('username' => $items), $attr)));
1851 break;
1852 case "quarantine_notification":
1853 process_edit_return(mailbox('edit', 'quarantine_notification', array_merge(array('username' => $items), $attr)));
1854 break;
1855 case "quarantine_category":
1856 process_edit_return(mailbox('edit', 'quarantine_category', array_merge(array('username' => $items), $attr)));
1857 break;
1858 case "qitem":
1859 process_edit_return(quarantine('edit', array_merge(array('id' => $items), $attr)));
1860 break;
1861 case "quarantine":
1862 process_edit_return(quarantine('edit', $attr));
1863 break;
1864 case "quota_notification":
1865 process_edit_return(quota_notification('edit', $attr));
1866 break;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001867 case "quota_notification_bcc":
1868 process_edit_return(quota_notification_bcc('edit', $attr));
1869 break;
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001870 case "domain-wide-footer":
1871 process_edit_return(mailbox('edit', 'domain_wide_footer', $attr));
1872 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001873 case "mailq":
1874 process_edit_return(mailq('edit', array_merge(array('qid' => $items), $attr)));
1875 break;
1876 case "time_limited_alias":
1877 process_edit_return(mailbox('edit', 'time_limited_alias', array_merge(array('address' => $items), $attr)));
1878 break;
1879 case "mailbox":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001880 switch ($object) {
1881 case "template":
1882 process_edit_return(mailbox('edit', 'mailbox_templates', array_merge(array('ids' => $items), $attr)));
1883 break;
1884 default:
1885 process_edit_return(mailbox('edit', 'mailbox', array_merge(array('username' => $items), $attr)));
1886 break;
1887 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001888 break;
1889 case "syncjob":
1890 process_edit_return(mailbox('edit', 'syncjob', array_merge(array('id' => $items), $attr)));
1891 break;
1892 case "filter":
1893 process_edit_return(mailbox('edit', 'filter', array_merge(array('id' => $items), $attr)));
1894 break;
1895 case "resource":
1896 process_edit_return(mailbox('edit', 'resource', array_merge(array('name' => $items), $attr)));
1897 break;
1898 case "domain":
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001899 switch ($object) {
1900 case "template":
1901 process_edit_return(mailbox('edit', 'domain_templates', array_merge(array('ids' => $items), $attr)));
1902 break;
1903 default:
1904 process_edit_return(mailbox('edit', 'domain', array_merge(array('domain' => $items), $attr)));
1905 break;
1906 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001907 break;
1908 case "rl-domain":
1909 process_edit_return(ratelimit('edit', 'domain', array_merge(array('object' => $items), $attr)));
1910 break;
1911 case "rl-mbox":
1912 process_edit_return(ratelimit('edit', 'mailbox', array_merge(array('object' => $items), $attr)));
1913 break;
1914 case "user-acl":
1915 process_edit_return(acl('edit', 'user', array_merge(array('username' => $items), $attr)));
1916 break;
1917 case "da-acl":
1918 process_edit_return(acl('edit', 'domainadmin', array_merge(array('username' => $items), $attr)));
1919 break;
1920 case "alias-domain":
1921 process_edit_return(mailbox('edit', 'alias_domain', array_merge(array('alias_domain' => $items), $attr)));
1922 break;
1923 case "spam-score":
1924 process_edit_return(mailbox('edit', 'spam_score', array_merge(array('username' => $items), $attr)));
1925 break;
1926 case "domain-admin":
1927 process_edit_return(domain_admin('edit', array_merge(array('username' => $items), $attr)));
1928 break;
1929 case "admin":
1930 process_edit_return(admin('edit', array_merge(array('username' => $items), $attr)));
1931 break;
1932 case "fwdhost":
1933 process_edit_return(fwdhost('edit', array_merge(array('fwdhost' => $items), $attr)));
1934 break;
1935 case "fail2ban":
1936 process_edit_return(fail2ban('edit', array_merge(array('network' => $items), $attr)));
1937 break;
1938 case "ui_texts":
1939 process_edit_return(customize('edit', 'ui_texts', $attr));
1940 break;
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001941 case "ip_check":
1942 process_edit_return(customize('edit', 'ip_check', $attr));
1943 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001944 case "self":
1945 if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
1946 process_edit_return(domain_admin('edit', $attr));
1947 }
1948 elseif ($_SESSION['mailcow_cc_role'] == "user") {
1949 process_edit_return(edit_user_account($attr));
1950 }
1951 break;
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001952 case "cors":
1953 process_edit_return(cors('edit', $attr));
1954 break;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001955 // return no route found if no case is matched
1956 default:
1957 http_response_code(404);
1958 echo json_encode(array(
1959 'type' => 'error',
1960 'msg' => 'route not found'
1961 ));
1962 exit();
1963 }
1964 break;
1965 // return no route found if no case is matched
1966 default:
1967 http_response_code(404);
1968 echo json_encode(array(
1969 'type' => 'error',
1970 'msg' => 'route not found'
1971 ));
1972 exit();
1973 }
1974}
1975if ($_SESSION['mailcow_cc_api'] === true) {
1976 if (isset($_SESSION['mailcow_cc_api']) && $_SESSION['mailcow_cc_api'] === true) {
1977 unset($_SESSION['return']);
1978 }
1979}