2017-05-17 01:20:58 +08:00
< ? php
2017-05-30 03:51:06 +08:00
require_once $_SERVER [ 'DOCUMENT_ROOT' ] . '/modals/footer.php' ;
2018-08-04 02:31:33 +08:00
logger ();
2019-10-21 03:25:58 +08:00
$hash = $js_minifier -> getDataHash ();
$JSPath = '/tmp/' . $hash . '.js' ;
if ( ! file_exists ( $JSPath )) {
$js_minifier -> minify ( $JSPath );
cleanupJS ( $hash );
}
?>
< script src = " /cache/<?=basename( $JSPath )?> " ></ script >
2017-05-17 01:20:58 +08:00
< script >
2018-09-10 03:17:59 +08:00
< ? php
$lang_footer = json_encode ( $lang [ 'footer' ]);
$lang_acl = json_encode ( $lang [ 'acl' ]);
$lang_tfa = json_encode ( $lang [ 'tfa' ]);
2020-11-16 02:32:37 +08:00
$lang_fido2 = json_encode ( $lang [ 'fido2' ]);
2018-09-10 03:17:59 +08:00
echo " var lang_footer = " . $lang_footer . " ; \n " ;
echo " var lang_acl = " . $lang_acl . " ; \n " ;
echo " var lang_tfa = " . $lang_tfa . " ; \n " ;
2020-11-16 02:32:37 +08:00
echo " var lang_fido2 = " . $lang_fido2 . " ; \n " ;
2018-09-10 03:17:59 +08:00
echo " var docker_timeout = " . $DOCKER_TIMEOUT * 1000 . " ; \n " ;
?>
2017-10-21 16:07:06 +08:00
$ ( window ) . scroll ( function () {
sessionStorage . scrollTop = $ ( this ) . scrollTop ();
});
2017-05-17 01:20:58 +08:00
// Select language and reopen active URL without POST
function setLang ( sel ) {
$ . post ( " <?= $_SERVER['REQUEST_URI'] ; ?> " , { lang : sel } );
window . location . href = window . location . pathname + window . location . search ;
}
2020-11-16 02:32:37 +08:00
// FIDO2 functions
function arrayBufferToBase64 ( buffer ) {
let binary = '' ;
let bytes = new Uint8Array ( buffer );
let len = bytes . byteLength ;
for ( let i = 0 ; i < len ; i ++ ) {
binary += String . fromCharCode ( bytes [ i ] );
}
return window . btoa ( binary );
}
function recursiveBase64StrToArrayBuffer ( obj ) {
let prefix = '=?BINARY?B?' ;
let suffix = '?=' ;
if ( typeof obj === 'object' ) {
for ( let key in obj ) {
if ( typeof obj [ key ] === 'string' ) {
let str = obj [ key ];
if ( str . substring ( 0 , prefix . length ) === prefix && str . substring ( str . length - suffix . length ) === suffix ) {
str = str . substring ( prefix . length , str . length - suffix . length );
let binary_string = window . atob ( str );
let len = binary_string . length ;
let bytes = new Uint8Array ( len );
for ( let i = 0 ; i < len ; i ++ ) {
bytes [ i ] = binary_string . charCodeAt ( i );
}
obj [ key ] = bytes . buffer ;
}
} else {
recursiveBase64StrToArrayBuffer ( obj [ key ]);
}
}
}
}
2018-08-04 02:31:33 +08:00
$ ( window ) . load ( function () {
2018-01-25 20:22:17 +08:00
$ ( " .overlay " ) . hide ();
});
2017-05-17 01:20:58 +08:00
$ ( document ) . ready ( function () {
2019-03-25 19:33:58 +08:00
$ ( document ) . on ( 'shown.bs.modal' , function ( e ) {
modal_id = $ ( e . relatedTarget ) . data ( 'target' );
$ ( modal_id ) . attr ( " aria-hidden " , " false " );
});
2018-09-10 03:17:59 +08:00
// TFA, CSRF, Alerts in footer.inc.php
// Other general functions in mailcow.js
2018-08-04 02:31:33 +08:00
< ? php
$alertbox_log_parser = alertbox_log_parser ( $_SESSION );
if ( is_array ( $alertbox_log_parser )) {
2018-08-14 05:20:40 +08:00
foreach ( $alertbox_log_parser as $log ) {
2020-05-21 02:37:52 +08:00
$alerts [ $log [ 'type' ]][] = $log [ 'msg' ];
}
foreach ( $alerts as $alert_type => $alert_msg ) {
2018-08-04 02:31:33 +08:00
?>
2020-05-21 02:37:52 +08:00
mailcow_alert_box ( < ? = json_encode ( implode ( '<hr class="alert-hr">' , $alert_msg )); ?> , <?=$alert_type;?>);
2018-08-04 02:31:33 +08:00
< ? php
2018-08-14 05:20:40 +08:00
}
2018-08-04 02:31:33 +08:00
unset ( $_SESSION [ 'return' ]);
}
?>
2017-05-17 01:20:58 +08:00
// Confirm TFA modal
< ? php if ( isset ( $_SESSION [ 'pending_tfa_method' ])) : ?>
$ ( '#ConfirmTFAModal' ) . modal ({
backdrop : 'static' ,
keyboard : false
});
2019-09-25 00:34:08 +08:00
$ ( '#u2f_status_auth' ) . html ( '<p><span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ' + lang_tfa . init_u2f + '</p>' );
2017-05-17 01:20:58 +08:00
$ ( '#ConfirmTFAModal' ) . on ( 'shown.bs.modal' , function (){
[Docker API] Use TLS encryption for communication with "on-the-fly" created key paris (non-exposed)
[Docker API] Create pipe to pass Rspamd UI worker password
[Dovecot] Pull Spamassassin ruleset to be read by Rspamd (MANY THANKS to Peer Heinlein!)
[Dovecot] Garbage collector for deleted maildirs (set keep time via MAILDIR_GC_TIME which defaults to 1440 minutes)
[Web] Flush memcached after mailbox item changes, fixes #1808
[Web] Fix duplicate IDs, fixes #1792
[Compose] Use SQL sockets
[PHP-FPM] Update APCu and Redis libs
[Dovecot] Encrypt maildir with global key pair in crypt-vol-1 (BACKUP!), also fixes #1791
[Web] Fix deletion of spam aliases
[Helper] Add "crypt" to backup script
[Helper] Override file for external SQL socket (not supported!)
[Compose] New images for Rspamd, PHP-FPM, SOGo, Dovecot, Docker API, Watchdog, ACME, Postfix
2018-09-30 04:01:23 +08:00
$ ( this ) . find ( 'input[name=token]' ) . focus ();
2017-05-17 01:20:58 +08:00
// If U2F
if ( document . getElementById ( " u2f_auth_data " ) !== null ) {
$ . ajax ({
type : " GET " ,
cache : false ,
dataType : 'script' ,
2018-02-11 05:42:46 +08:00
url : " /api/v1/get/u2f-authentication/<?= (isset( $_SESSION['pending_mailcow_cc_username'] )) ? rawurlencode( $_SESSION['pending_mailcow_cc_username'] ) : null; ?> " ,
2017-11-21 16:33:22 +08:00
complete : function ( data ){
2018-09-10 03:17:59 +08:00
$ ( '#u2f_status_auth' ) . html ( lang_tfa . waiting_usb_auth );
2017-05-17 01:20:58 +08:00
data ;
2017-11-21 16:33:22 +08:00
setTimeout ( function () {
console . log ( " Ready to authenticate " );
u2f . sign ( appId , challenge , registeredKeys , function ( data ) {
var form = document . getElementById ( 'u2f_auth_form' );
var auth = document . getElementById ( 'u2f_auth_data' );
console . log ( " Authenticate callback " , data );
auth . value = JSON . stringify ( data );
form . submit ();
});
}, 1000 );
2017-05-17 01:20:58 +08:00
}
});
}
});
2018-02-17 05:39:33 +08:00
$ ( '#ConfirmTFAModal' ) . on ( 'hidden.bs.modal' , function (){
$ . ajax ({
type : " GET " ,
cache : false ,
dataType : 'script' ,
url : '/inc/ajax/destroy_tfa_auth.php' ,
complete : function ( data ){
window . location = window . location . href . split ( " # " )[ 0 ];
}
});
});
2017-05-17 01:20:58 +08:00
< ? php endif ; ?>
2020-11-16 02:32:37 +08:00
// Validate FIDO2
$ ( " #fido2-login " ) . click ( function (){
$ ( '#fido2-alerts' ) . html ();
if ( ! window . fetch || ! navigator . credentials || ! navigator . credentials . create ) {
window . alert ( 'Browser not supported.' );
return ;
}
window . fetch ( " /api/v1/get/fido2-get-args " , { method : 'GET' , cache : 'no-cache' }) . then ( function ( response ) {
return response . json ();
}) . then ( function ( json ) {
if ( json . success === false ) {
throw new Error ();
}
recursiveBase64StrToArrayBuffer ( json );
return json ;
}) . then ( function ( getCredentialArgs ) {
return navigator . credentials . get ( getCredentialArgs );
}) . then ( function ( cred ) {
return {
id : cred . rawId ? arrayBufferToBase64 ( cred . rawId ) : null ,
clientDataJSON : cred . response . clientDataJSON ? arrayBufferToBase64 ( cred . response . clientDataJSON ) : null ,
authenticatorData : cred . response . authenticatorData ? arrayBufferToBase64 ( cred . response . authenticatorData ) : null ,
signature : cred . response . signature ? arrayBufferToBase64 ( cred . response . signature ) : null
};
}) . then ( JSON . stringify ) . then ( function ( AuthenticatorAttestationResponse ) {
return window . fetch ( " /api/v1/process/fido2-args " , { method : 'POST' , body : AuthenticatorAttestationResponse , cache : 'no-cache' });
}) . then ( function ( response ) {
return response . json ();
}) . then ( function ( json ) {
if ( json . success ) {
window . location = window . location . href . split ( " # " )[ 0 ];
} else {
throw new Error ();
}
}) . catch ( function ( err ) {
2020-11-16 15:55:45 +08:00
if ( typeof err . message === 'undefined' ) {
mailcow_alert_box ( lang_fido2 . fido2_validation_failed , " danger " );
} else {
mailcow_alert_box ( lang_fido2 . fido2_validation_failed + " :<br><i> " + err . message + " </i> " , " danger " );
}
2020-11-16 02:32:37 +08:00
});
});
// Set TFA/FIDO2
$ ( " #register-fido2 " ) . click ( function (){
$ ( " option:selected " ) . prop ( " selected " , false );
if ( ! window . fetch || ! navigator . credentials || ! navigator . credentials . create ) {
window . alert ( 'Browser not supported.' );
return ;
}
window . fetch ( " /api/v1/get/fido2-registration/<?= (isset( $_SESSION['mailcow_cc_username'] )) ? rawurlencode( $_SESSION['mailcow_cc_username'] ) : null; ?> " , { method : 'GET' , cache : 'no-cache' }) . then ( function ( response ) {
return response . json ();
}) . then ( function ( json ) {
if ( json . success === false ) {
throw new Error ( json . msg );
}
recursiveBase64StrToArrayBuffer ( json );
return json ;
}) . then ( function ( createCredentialArgs ) {
console . log ( createCredentialArgs );
return navigator . credentials . create ( createCredentialArgs );
}) . then ( function ( cred ) {
return {
clientDataJSON : cred . response . clientDataJSON ? arrayBufferToBase64 ( cred . response . clientDataJSON ) : null ,
attestationObject : cred . response . attestationObject ? arrayBufferToBase64 ( cred . response . attestationObject ) : null
};
}) . then ( JSON . stringify ) . then ( function ( AuthenticatorAttestationResponse ) {
return window . fetch ( " /api/v1/add/fido2-registration " , { method : 'POST' , body : AuthenticatorAttestationResponse , cache : 'no-cache' });
}) . then ( function ( response ) {
return response . json ();
}) . then ( function ( json ) {
if ( json . success ) {
window . location = window . location . href . split ( " # " )[ 0 ];
} else {
throw new Error ( json . msg );
}
}) . catch ( function ( err ) {
$ ( '#fido2-alerts' ) . html ( '<span class="text-danger"><b>' + err . message + '</b></span>' );
});
});
2017-05-17 01:20:58 +08:00
$ ( '#selectTFA' ) . change ( function () {
if ( $ ( this ) . val () == " yubi_otp " ) {
$ ( '#YubiOTPModal' ) . modal ( 'show' );
$ ( " option:selected " ) . prop ( " selected " , false );
}
if ( $ ( this ) . val () == " totp " ) {
$ ( '#TOTPModal' ) . modal ( 'show' );
2019-03-19 15:45:08 +08:00
request_token = $ ( '#tfa-qr-img' ) . data ( 'totp-secret' );
$ . ajax ({
url : '/inc/ajax/qr_gen.php' ,
data : {
token : request_token ,
},
}) . done ( function ( result ) {
$ ( " #tfa-qr-img " ) . attr ( " src " , result );
});
2017-05-17 01:20:58 +08:00
$ ( " option:selected " ) . prop ( " selected " , false );
}
if ( $ ( this ) . val () == " u2f " ) {
$ ( '#U2FModal' ) . modal ( 'show' );
$ ( " option:selected " ) . prop ( " selected " , false );
2019-09-25 00:34:08 +08:00
$ ( " #start_u2f_register " ) . click ( function (){
$ ( '#u2f_return_code' ) . html ( '' );
$ ( '#u2f_return_code' ) . hide ();
$ ( '#u2f_status_reg' ) . html ( '<p><span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ' + lang_tfa . init_u2f + '</p>' );
$ . ajax ({
type : " GET " ,
cache : false ,
dataType : 'script' ,
url : " /api/v1/get/u2f-registration/<?= (isset( $_SESSION['mailcow_cc_username'] )) ? rawurlencode( $_SESSION['mailcow_cc_username'] ) : null; ?> " ,
complete : function ( data ){
data ;
setTimeout ( function () {
console . log ( " Ready to register " );
$ ( '#u2f_status_reg' ) . html ( lang_tfa . waiting_usb_register );
u2f . register ( appId , registerRequests , registeredKeys , function ( deviceResponse ) {
var form = document . getElementById ( 'u2f_reg_form' );
var reg = document . getElementById ( 'u2f_register_data' );
console . log ( " Register callback: " , data );
if ( deviceResponse . errorCode && deviceResponse . errorCode != 0 ) {
var u2f_return_code = document . getElementById ( 'u2f_return_code' );
u2f_return_code . style . display = u2f_return_code . style . display === 'none' ? '' : null ;
if ( deviceResponse . errorCode == " 4 " ) {
deviceResponse . errorCode = " 4 - The presented device is not eligible for this request. For a registration request this may mean that the token is already registered, and for a sign request it may mean that the token does not know the presented key handle " ;
}
else if ( deviceResponse . errorCode == " 5 " ) {
deviceResponse . errorCode = " 5 - Timeout reached before request could be satisfied. " ;
}
u2f_return_code . innerHTML = lang_tfa . error_code + ': ' + deviceResponse . errorCode + ' ' + lang_tfa . reload_retry ;
return ;
}
reg . value = JSON . stringify ( deviceResponse );
form . submit ();
});
}, 1000 );
}
});
2017-05-17 01:20:58 +08:00
});
}
if ( $ ( this ) . val () == " none " ) {
$ ( '#DisableTFAModal' ) . modal ( 'show' );
$ ( " option:selected " ) . prop ( " selected " , false );
}
});
2019-09-22 21:10:56 +08:00
// Reload after session timeout
2019-10-24 04:09:10 +08:00
var session_lifetime = < ? = (( int ) $SESSION_LIFETIME * 1000 ) + 15000 ; ?> ;
2019-09-22 21:10:56 +08:00
< ? php
if ( isset ( $_SESSION [ 'mailcow_cc_username' ])) :
?>
setTimeout ( function () {
location . reload ();
}, session_lifetime );
< ? php
endif ;
?>
2017-05-17 01:20:58 +08:00
// CSRF
[Docker API] Use TLS encryption for communication with "on-the-fly" created key paris (non-exposed)
[Docker API] Create pipe to pass Rspamd UI worker password
[Dovecot] Pull Spamassassin ruleset to be read by Rspamd (MANY THANKS to Peer Heinlein!)
[Dovecot] Garbage collector for deleted maildirs (set keep time via MAILDIR_GC_TIME which defaults to 1440 minutes)
[Web] Flush memcached after mailbox item changes, fixes #1808
[Web] Fix duplicate IDs, fixes #1792
[Compose] Use SQL sockets
[PHP-FPM] Update APCu and Redis libs
[Dovecot] Encrypt maildir with global key pair in crypt-vol-1 (BACKUP!), also fixes #1791
[Web] Fix deletion of spam aliases
[Helper] Add "crypt" to backup script
[Helper] Override file for external SQL socket (not supported!)
[Compose] New images for Rspamd, PHP-FPM, SOGo, Dovecot, Docker API, Watchdog, ACME, Postfix
2018-09-30 04:01:23 +08:00
$ ( '<input type="hidden" value="<?= $_SESSION[' CSRF '][' TOKEN ']; ?>">' ) . attr ( 'name' , 'csrf_token' ) . appendTo ( 'form' );
2017-10-21 16:07:06 +08:00
if ( sessionStorage . scrollTop != " undefined " ) {
$ ( window ) . scrollTop ( sessionStorage . scrollTop );
}
2017-05-17 01:20:58 +08:00
});
</ script >
2019-10-24 04:09:10 +08:00
< div class = " container footer " >
2020-05-11 17:52:02 +08:00
< ? php if ( ! empty ( $UI_TEXTS [ 'ui_footer' ])) { ?>
2020-08-16 17:54:06 +08:00
< hr >< span class = " rot-enc " >< ? = str_rot13 ( $UI_TEXTS [ 'ui_footer' ]); ?> </span>
2020-05-11 17:52:02 +08:00
< ? php } ?>
2019-09-29 02:00:04 +08:00
</ div >
2017-05-17 01:20:58 +08:00
</ body >
</ html >
< ? php
$stmt = null ;
2017-12-09 20:17:15 +08:00
$pdo = null ;