<?php
namespace Zotlabs\Module;
use Zotlabs\Lib\Connect;
use Zotlabs\Daemon\Master;
require_once('include/security.php');
/**
*
* @version 2.0.0
* @author hilmar runge
* @since 2020-03-03
* Check verification pin
* input field email address
* input field pin (told during register)
* check duty
* check startup and expire
* compare email address
* check pin
* limited tries to enter the correct pin/pass 2 handle via f2b
* on success create account and update register
*
*/
define ( 'REGISTER_AGREED', 0x0020 );
define ( 'REGISTER_DENIED', 0x0040 );
class Regate extends \Zotlabs\Web\Controller {
const MYP = 'ZAR'; //ZAR1x
const VERSION = '2.0.0';
function post() {
check_form_security_token_redirectOnErr('/', 'regate');
if ( argc() > 1 ) {
$did2 = hex2bin( substr( argv(1), 0, -1) );
$didx = substr( argv(1), -1 );
}
$msg = '';
$nextpage = '';
if ($did2) {
$nowhhmm = date('Hi');
$day = date('N');
$now = datetime_convert();
$ip = $_SERVER['REMOTE_ADDR'];
$isduty = zar_register_dutystate();
if (!$_SESSION['zar']['invite_in_progress'] && ($isduty['isduty'] !== false && $isduty['isduty'] != 1)) {
// normally, that should never happen here
// log suitable for fail2ban also
$logmsg = 'ZAR1230S Unexpected registration verification request for '
. get_config('system','sitename') . ' arrived from § ' . $ip . ' §';
zar_log($logmsg);
goaway(z_root());
}
// do we have a valid dId2 ?
if (($didx == 'a' && substr( $did2 , -2) == substr( base_convert( md5( substr( $did2, 1, -2) ),16 ,10), -2)) || ($didx == 'e') || ($didx == 'i')) {
// check startup and expiration via [=[register
$r = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_did2 = '%s' ORDER BY reg_created DESC ",
dbesc($did2)
);
if ($r && count($r)) {
$r = $r[0];
// check timeframe
if ($r['reg_startup'] <= $now && $r['reg_expires'] >= $now) {
if (isset($_POST['resend']) && $didx == 'e') {
$re = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_didx = 'e' AND reg_did2 = '%s' ORDER BY reg_created DESC ", dbesc($r['reg_did2']) );
if ($re) {
$re = $re[0];
$reonar = json_decode($re['reg_stuff'], true);
if ($reonar) {
$reonar['subject'] = 'Re,Fwd,' . $reonar['subject'];
$zm = zar_reg_mail($reonar);
$msg = (($zm) ? t('Email resent') : t('Email resend failed'));
zar_log((($zm) ? 'ZAR1238I' : 'ZAR1238E') . ' ' . $msg . ' ' . $r['reg_did2']);
info($msg);
return;
}
}
}
// check hash
if ( $didx == 'a' )
$acpin = (preg_match('/^[0-9]{6,6}$/', $_POST['acpin']) ? $_POST['acpin'] : false);
elseif ( $didx == 'e' )
$acpin = (preg_match('/^[0-9a-f]{24,24}$/', $_POST['acpin']) ? $_POST['acpin'] : false);
elseif ( $didx == 'i' )
$acpin = $r['reg_hash'];
else
$acpin = false;
if ( $acpin && ($r['reg_hash'] == $acpin )) {
$flags = $r['reg_flags'];
if (($flags & ACCOUNT_UNVERIFIED) == ACCOUNT_UNVERIFIED) {
// verification success
$msg_code = 'ZAR1237I';
$msg = t('Verification successful');
$reonar = json_decode( $r['reg_stuff'], true);
$reonar['valid'] = $now . ',' . $ip . ' ' . $did2 . ' ' . $msg_code . ' ' . $msg;
// clear flag
$flags &= $flags ^ ACCOUNT_UNVERIFIED;
// are we invited by the admin?
$isa = get_account_by_id($r['reg_uid']);
$isa = ($isa && ($isa['account_roles'] && ACCOUNT_ROLE_ADMIN));
// approve contra invite by admin
if ($isa && get_config('system','register_policy') == REGISTER_APPROVE) {
$flags &= $flags ^ ACCOUNT_PENDING;
}
// sth todo?
$vital = $flags == 0 ? 0 : 1;
// set flag
$flags |= REGISTER_AGREED;
zar_log($msg . ' ' . $did2 . ':flags' . $flags . ',rid' . $r['reg_id']);
q("START TRANSACTION");
$qu = q("UPDATE register SET reg_stuff = '%s', reg_vital = %d, reg_flags = %d "
." WHERE reg_id = %d ",
dbesc(json_encode($reonar)),
intval($vital),
intval($flags),
intval($r['reg_id'])
);
if (($flags & ACCOUNT_PENDING ) == ACCOUNT_PENDING) {
$nextpage = 'regate/' . bin2hex($did2) . $didx;
q("COMMIT");
}
elseif (($flags ^ REGISTER_AGREED) == 0) {
$cra = create_account_from_register([ 'reg_id' => $r['reg_id'] ]);
if ($cra['success']) {
q("COMMIT");
$msg = t('Account successfull created');
// zar_log($msg . ':' . print_r($cra, true));
zar_log('ZAR1238I ' . $msg . ' ' . $cra['account']['account_email']
. ' ' . $cra['account']['account_language']);
authenticate_success($cra['account'],null,true,false,true);
$nextpage = 'new_channel';
$auto_create = get_config('system', 'auto_channel_create', 1);
if($auto_create) {
$new_channel = ['success' => false];
// We do not reserve a channel_address before the registration is verified
// and possibly approved by the admin.
// If the provided channel_address has been claimed meanwhile,
// we will proceed to /new_channel.
if(isset($reonar['chan.did1']) && check_webbie([$reonar['chan.did1']])) {
// prepare channel creation
if($reonar['chan.name'])
set_aconfig($cra['account']['account_id'], 'register', 'channel_name', $reonar['chan.name']);
if($reonar['chan.did1'])
set_aconfig($cra['account']['account_id'], 'register', 'channel_address', $reonar['chan.did1']);
$permissions_role = get_config('system','default_permissions_role');
if($permissions_role)
set_aconfig($cra['account']['account_id'], 'register', 'permissions_role', $permissions_role);
// create channel
$new_channel = auto_channel_create($cra['account']['account_id']);
if($new_channel['success']) {
$channel_id = $new_channel['channel']['channel_id'];
// If we have an inviter, connect.
if ($didx === 'i' && intval($r['reg_byc'])) {
$invite_channel = channelx_by_n($r['reg_byc']);
if ($invite_channel) {
$f = Connect::connect($new_channel['channel'], $invite_channel['xchan_addr']);
if ($f['success']) {
$can_view_stream = intval(get_abconfig($channel_id, $f['abook']['abook_xchan'], 'their_perms', 'view_stream'));
// If we can view their stream, pull in some posts
if ($can_view_stream) {
Master::Summon(['Onepoll', $f['abook']['abook_id']]);
}
}
}
}
change_channel($channel_id);
$nextpage = 'profiles/' . $channel_id;
$msg_code = 'ZAR1239I';
$msg = t('Channel successfull created') . ' ' . $did2;
}
}
if(!$new_channel['success']) {
$msg_code = 'ZAR1239E';
$msg = t('Automatic channel creation failed. Please create a channel.') . ' ' . $did2;
$nextpage = 'new_channel?name=' . $reonar['chan.name'];
}
zar_log($msg_code . ' ' . $msg . ' ' . $reonar['chan.did1'] . ' (' . $reonar['chan.name'] . ')');
}
unset($_SESSION['login_return_url']);
}
else {
q("ROLLBACK");
$msg_code = 'ZAR1238E';
$msg = t('Account creation error');
zar_log($msg_code . ' ' . $msg . ': ' . print_r($cra, true));
}
}
else {
// new flags implemented and not recognized or sth like
zar_log('ZAR1237D unexpected,' . $flags);
}
}
else {
// nothing to confirm
$msg_code = 'ZAR1236E';
$msg = t('Verify failed');
}
}
else {
$msg_code = 'ZAR1235E';
$msg = t('Token verification failed');
}
}
else {
$msg_code = 'ZAR1234W';
$msg = t('Request not inside time frame');
//info($r[0]['reg_startup'] . EOL . $r[0]['reg_expire'] );
}
}
else {
$msg_code = 'ZAR1232E';
$msg = t('Identity unknown');
zar_log($msg_code . ' ' . $msg . ':' . $did2 . $didx);
}
}
else {
$msg_code = 'ZAR1231E';
$msg = t('dId2 mistaken');
zar_log($msg_code . ' ' . $msg);
}
}
if ($msg > '') info($msg);
goaway( z_root() . '/' . $nextpage );
}
function get() {
if (argc() == 1) {
if(isset($_GET['reg_id'])) {
if ( preg_match('/^.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $_GET['reg_id'] ) ) {
// dId2 E email
goaway(z_root() . '/regate/' . bin2hex($_GET['reg_id']) . 'e' );
}
if ( preg_match('/^d{1,1}[0-9]{5,10}$/', $_GET['reg_id'] ) ) {
// dId2 A artifical & anonymous
goaway(z_root() . '/regate/' . bin2hex($_GET['reg_id']) . 'a' );
}
notice(t('Identity unknown') . EOL);
}
$o = replace_macros(get_markup_template('plain.tpl'), [
'$title' => t('Your Registration ID'),
'$now' => '<form action="regate" method="get"><input type="text" name="reg_id" class="form-control form-group"><button class="btn btn-primary float-right">Submit</button></form>'
]);
return $o;
}
$isduty = zar_register_dutystate();
$nowfmt = $isduty['nowfmt'];
$atform = $isduty['atform'];
if ($_SESSION['zar']['delayed']) {
$o = replace_macros(get_markup_template('regate_pre.tpl'), [
'$title' => t('Registration verification'),
'$now' => $nowfmt,
'$id' => $_SESSION['zar']['id'],
'$pin' => $_SESSION['zar']['pin'],
'$regdelay' => $_SESSION['zar']['regdelay'],
'$regexpire' => $_SESSION['zar']['regexpire'],
'$strings' => [
t('Hold on, you can start verification in'),
t('Please remember your verification token for ID'),
'',
t('Token validity')
]
]);
unset($_SESSION['zar']['delayed']);
return $o;
}
if (argc() < 2)
return;
$did2 = hex2bin( substr( argv(1), 0, -1) );
$didx = substr( argv(1), -1 );
$deny = argc() > 2 ? argv(2) : '';
$deny = preg_match('/^[0-9a-f]{8,8}$/', $deny) ? hex2bin($deny) : false;
$now = datetime_convert();
$ip = $_SERVER['REMOTE_ADDR'];
$pin = '';
if(isset($_SESSION['zar']['pin'])) {
$pin = $_SESSION['zar']['pin'];
unset($_SESSION['zar']['pin']);
}
// do we have a valid dId2 ?
if (($didx == 'a' && substr( $did2 , -2) == substr( base_convert( md5( substr( $did2, 1, -2) ),16 ,10), -2)) || ($didx == 'e') || ($didx == 'i')) {
$r = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_didx = '%s' AND reg_did2 = '%s' ORDER BY reg_created DESC",
dbesc($didx),
dbesc($did2)
);
if ($r && count($r) && $r[0]['reg_flags'] &= (ACCOUNT_UNVERIFIED | ACCOUNT_PENDING)) {
$r = $r[0];
// provide a button in case
$resend = (($r['reg_didx'] == 'e') ? t('Resend email') : '');
// is still only instance admins intervention required?
if ($r['reg_flags'] == ACCOUNT_PENDING) {
$o = replace_macros(get_markup_template('regate_post.tpl'), [
'$title' => t('Registration status'),
'$id' => $did2,
'$strings' => [
t('Verification successful!'),
t('Your login ID is'),
t('After your account has been approved by our administrator you will be able to login with your login ID and your provided password.')
]
]);
}
else {
if ($deny) {
if (substr($r['reg_hash'],0,4) == $deny) {
zar_log('ZAR1134S email verfication denied ' . $did2);
$o = replace_macros(get_markup_template('plain.tpl'), [
'$title' => t('Registration request revoked'),
'$infos' => t('Sorry for any inconvience. Thank you for your response.')
]);
$reonar = json_decode( $r['reg_stuff'], true);
$reonar['deny'] = $now . ',' . $ip . ' ' . $did2 . ' ' . $msg;
$flags = ( $r['reg_flags'] &= ( $r['reg_flags'] ^ ACCOUNT_UNVERIFIED) )
| ( $r['reg_flags'] |= REGISTER_DENIED);
$rd = q("UPDATE register SET reg_stuff='%s', reg_vital=0, reg_flags=%d WHERE reg_id = %d ",
dbesc(json_encode($reonar)),
intval($flags),
intval($r['reg_id'])
);
}
else {
zar_log('ZAR1135E not awaited url parameter received');
goaway(z_root);
}
}
else {
if ( $r['reg_startup'] <= $now && $r['reg_expires'] >= $now) {
$o = replace_macros(get_markup_template('regate.tpl'), [
'$form_security_token' => get_form_security_token("regate"),
'$title' => t('Registration verification'),
'$desc' => t('Please enter your verification token for ID'),
'$email_extra' => (($didx === 'e') ? t('Please check your email!') : ''),
'$id' => $did2,
// we might consider to not provide $pin if a registration delay is configured
// and the pin turns out to be readable by bots
'$pin' => $pin,
'$did2' => bin2hex($did2) . $didx,
'$now' => $nowfmt,
'$atform' => $atform,
'$resend' => $resend,
'$submit' => t('Submit'),
'$acpin' => [ 'acpin', t('Verification token'),'','' ]
]);
}
else {
// expired ?
if ( $now > $r['reg_expires'] ) {
$rd = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d ",
intval($r['reg_id'])
);
$o = replace_macros(get_markup_template('plain.tpl'), [
'$infos' => t('ID expired'),
]);
return $o;
}
$email_extra = (($didx === 'e') ? t('Please check your email!') : '');
$o = replace_macros(get_markup_template('regate_pre.tpl'), [
'$title' => t('Registration verification'),
'$now' => $nowfmt,
'$id' => $did2,
'$countdown' => datetime_convert('UTC', 'UTC', $r['reg_startup'], 'c'),
'$strings' => [
t('Hold on, you can start verification in'),
t('You will require the verification token for ID'),
$email_extra
]
]);
}
}
}
}
else {
$msg = t('Unknown or expired ID');
zar_log('ZAR1132E ' . $msg . ':' . $did2 . ',' . $didx);
$o = replace_macros(get_markup_template('plain.tpl'), [
'$title' => $msg,
'$now' => $nowfmt,
'$infos' => $msg
]);
}
}
else {
$msg = 'ZAR1131E ' . t('dId2 malformed');
// $log = ' from § ' . $ip . ' §' . ' (' . dbesc($did2) . ')';
zar_log($msg);
$o = replace_macros(get_markup_template('plain.tpl'), [
'$title' => $title,
'$now' => $nowfmt,
'$infos' => $msg
]);
}
return $o;
}
}