# vim: set cindent expandtab ts=4 sw=4:
#
# Copyright (c) 1998-2005 Chi-Keung Ho. All rights reserved.
#
# This programe is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# ExtMan - web interface to manage virtual accounts
# $Id$
package Ext::MgrApp::Signup;
use strict;
use Exporter;

use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter Ext::MgrApp);
use POSIX qw(strftime);
use Ext::Utils; # import url2str
use Ext::MgrApp;
use Ext::CaptCha;
use vars qw($lang_charset %lang_signup);
use Ext::Lang;
use Fcntl qw(:flock);

sub init {
    my $self = shift;
    $self->register;
    $self->SUPER::init(@_);
    return unless($self->valid||$self->permit);

    $self->add_methods(step1 => \&step1);
    $self->add_methods(step2 => \&step2);
    $self->add_methods(do_signup => \&do_signup);
    $self->{default_mode} = 'step1';
    $self->_initme;
    $self->{tpl}->assign( DOMAIN => $self->{query}->cgi('domain') );

    $self;
}

sub _initme {
    initlang($_[0]->{sysconfig}->{'SYS_LANG'}, __PACKAGE__);
    $_[0]->{tpl}->assign( lang_charset => $lang_charset );
    $_[0]->{tpl}->assign( \%lang_signup );
}

sub check {
    my $self = shift;
    my $q = $self->{query};
    my $tpl = $self->{tpl};
    my $mgr = $self->{backend};
    my $domain = $q->cgi('domain');

    if (!$domain) {
        $tpl->assign( ERROR => $lang_signup{'err_domain'} );
        return;
    }

    my $info = $mgr->get_domain_info($domain);
    if (!$info) {
        $tpl->assign( ERROR => sprintf($lang_signup{'err_nosuch_domain'}, $domain) );
        return;
    }

    if (!$info->{can_signup}) {
        $tpl->assign( ERROR => sprintf($lang_signup{'err_nosignup'}, $domain) );
        return;
    }
    $self->{_curinfo} = $info;
}

sub user_cfm_info {
    my $self = shift;
    my $sys = $self->{sysconfig};
    my ($account, $tname, $idcard) = @_;
    my $file = $sys->{SYS_VALIDATION_FILE} || "$sys->{SYS_CONFIG}/reg_validation.txt";
    my $buf;

    open (FD, "< $file") or die $!;
    flock (FD, LOCK_EX);
    while (<FD>) {
        chomp;
        my @m = split(/\s+/, $_);
        next unless $m[1] eq $account && $m[2] eq $tname && $m[3] eq $idcard;
        $buf = \@m;
        last;
    }
    flock (FD, LOCK_UN);

    return $buf;
}

sub toggle_user_status {
    my $self = shift;
    my $sys = $self->{sysconfig};
    my ($account, $tname, $idcard) = @_;
    my $file = $sys->{SYS_VALIDATION_FILE} || "$sys->{SYS_CONFIG}/reg_validation.txt";

    my @lines;
    open (FD, " < $file") or die $!;
    flock (FD, LOCK_EX);
    while (<FD>) {
        chomp;
        my @m = split(/\s+/, $_);
        $m[0] = 1 if ($m[1] eq $account && $m[2] eq $tname && $m[3] eq $idcard);
        push @lines, \@m;
    }
    flock (FD, LOCK_UN);
    close FD;

    open (FD, "> $file.tmp") or die $!;
    flock (FD, LOCK_EX);
    for my $line (@lines) {
        print FD join("\t", @$line) . "\n";
    }
    close FD;
    flock (FD, LOCK_UN);

    rename ("$file.tmp", $file);
}

sub step1 {
    my $self = shift;
    my $mgr = $self->{backend};
    my $q = $self->{query};
    my $tpl = $self->{tpl};

    $self->check;
    $tpl->assign( STEP1 => 1);
}

sub step2 {
    my $self = shift;
    my $mgr = $self->{backend};
    my $q = $self->{query};
    my $tpl = $self->{tpl};
    my ($user, $domain) = (lc $q->cgi('username'), lc $q->cgi('domain'));

    my $account = $q->cgi('id1');
    my $tname = $q->cgi('tname');
    my $idcard = $q->cgi('idcard');

    $tpl->assign(
        ID1 => $account,
        IDCARD => $idcard,
        TNAME => $tname,
        NAME => $tname,
    );

    if (!$self->check) {
        $tpl->assign(STEP1 => 1);
        return 0;
    }

    if (!$self->sanity_username($user)) {                
        $tpl->assign(ERROR => $lang_signup{'bad_username'});                        
        $tpl->assign(DOMAIN => $domain);
        $tpl->assign(STEP1 => 1);
        return 0;                            
    }

    if ($mgr->get_user_info("$user\@$domain")) {
        $tpl->assign(ERROR => sprintf($lang_signup{'user_exist'}, $user));
        $self->step1;
        return 0;
    }

    if (!$account or !$tname or !$idcard) {
        $tpl->assign( ERROR => '个人资料输入不齐全');
        $self->step1;
        return;
    }

    if (my $buf = $self->user_cfm_info($account, $tname, $idcard)) {
        if ($buf->[0] == 1) {
            $tpl->assign( ERROR => '您已经注册过邮箱了!');
            $self->step1;
            return;
        }
    } else {
        $tpl->assign( ERROR => '您输入的个人资料不正确!' );
        $self->step1;
        return;
    }

    $tpl->assign(
        STEP2 => 1,
        USERNAME => $user,
        SUCCESS => sprintf($lang_signup{hint_can_signup}, "$user"),
    );
}

sub do_signup {
    my $self = shift;
    my $tpl = $self->{tpl};
    my $mgr = $self->{backend};
    my $q = $self->{query};
    my $multiplier = $self->{sysconfig}->{SYS_QUOTA_MULTIPLIER} || '1048576';
    my ($user, $domain) = (lc $q->cgi('username'), lc $q->cgi('domain'));
    my $sys = $self->{sysconfig};

    my $account = $q->cgi('id1');
    my $tname = $q->cgi('tname');
    my $idcard = $q->cgi('idcard');

    $tpl->assign(
        ID1 => $account,
        IDCARD => $idcard,
        TNAME => $tname,
    );

    # recall step 1 again
    $self->check;

    if (!$self->sanity_username($user)) {
        $tpl->assign(ERROR => $lang_signup{'bad_username'});
        $tpl->assign(STEP1 => 1); # goto step1
        $tpl->assign(DOMAIN => $domain);
        return 0;
    }

    if (!$account or !$tname or !$idcard) {
        $tpl->assign( ERROR => '个人资料输入不齐全');
        $self->step1;
        return;
    }

    my $cfm_info = $self->user_cfm_info($account, $tname, $idcard);
    if ($cfm_info) {
        if ($cfm_info->[0] == 1) {
            $tpl->assign( ERROR => '您已经注册过邮箱了!');
            return;
        }
    } else {
        $tpl->assign( ERROR => '提交表单的用户信息有错!' );
        return;
    }

    if ($mgr->get_user_info("$user\@$domain")) {
        $tpl->assign(ERROR => sprintf($lang_signup{'user_exist'}, $user));
        $self->step1;
        return 0;
    } else {
        # new add user
        my $info = $self->{_curinfo}; # XXX current domain info structure
        my $pwd1 = $q->cgi('passwd1');
        my $pwd2 = $q->cgi('passwd2');

        $tpl->assign(
            NAME => $q->cgi('cn') || $tname,
            DOMAIN => $domain,
            USERNAME => $user,
        );
        # include all possible state:
        # 1) pwd1 null, pwd2 not null
        # 2) pwd2 null, pwd1 not null
        # 3) pwd2 and pwd1 not null, but not eq
        # 4) pwd1 null, pwd2 null
        if ( (!$pwd1 and !$pwd2) or ($pwd1 ne $pwd2) ) {
            $tpl->assign(ERROR => $lang_signup{'errinput_passwd'});
            $tpl->assign(STEP2 => 1);
            return 0;
        }

        if (!$q->cgi('cn')) {
            $tpl->assign(ERROR => $lang_signup{'errinput_cn'});
            $tpl->assign(STEP2 => 1);
            return 0;
        }

        if ($sys->{SYS_CAPTCHA_ON}) {
            my $data = $q->get_cookie('scode');
            my $raw = $q->cgi('vcode'); # verify code
            my $key = $sys->{SYS_CAPTCHA_KEY} || 'extmail';
            my $cap = Ext::CaptCha->new(key => $key);

            if (!$cap->verify($raw, $data)) {
                $tpl->assign(ERROR => $lang_signup{'err_vcode'}||'Bad verify Code');
                $tpl->assign(STEP2 => 1);
                return 0;
            }
        }

        my $rc = $self->domain_overusage(
            domain => $domain,
            quota => $info->{default_quota} || $self->num2quota(5*$multiplier),
            user => 1, # new add, must exists
            ndquota => $info->{default_ndquota} || $self->num2quota(5*$multiplier),
        );

        my $d_hashdir = $self->get_domain_hashdir($domain);
        my $u_hashdir = $self->gen_user_hashdir;
        my $path;
        if ($sys->{SYS_ISP_MODE} eq 'yes') {
            $path = ($d_hashdir ? "$d_hashdir/" : "").
            "$domain/" .($u_hashdir? "$u_hashdir/" : "").
                $user;
        } else {
            $path = "$domain/$user";
        }

        my $uid = $sys->{SYS_DEFAULT_UID};
        my $gid = $sys->{SYS_DEFAULT_GID};
        my $expire = $self->cvt2expire(
            strftime("%Y-%m-%d %H:%M:%S", localtime), 
            $info->{default_expire},
        );

        $rc = $mgr->add_user(
            mail => "$user\@$domain",
            domain => $domain,
            uid => $user,
            cn => $q->cgi('cn'),
            uidnumber => $uid,
            gidnumber => $gid,
            # new user attributes here
            create => strftime("%Y-%m-%d %H:%M:%S", localtime),
            expire => $expire,
            passwd => $pwd1,
            quota => $info->{default_quota} || $self->num2quota(5*$multiplier),
            maildir => "$path/Maildir/",
            homedir => $path,
            netdiskquota => $info->{default_ndquota} || $self->num2quota(5*$multiplier),
            active => 1,
            disablepwdchange => 0,
            disablesmtpd => $info->{disablesmtpd} ? 1 : 0,
            disablesmtp => $info->{disablesmtp} ? 1 : 0,
            disablewebmail => $info->{disablewebmail} ? 1 : 0,
            disablenetdisk => $info->{disablenetdisk} ? 1 : 0,
            disablepop3 => $info->{disablepop3} ? 1 : 0,
            disableimap => $info->{disableimap} ? 1 : 0,
        ) unless ($rc);

        if ($rc) {
            $tpl->assign(ERROR => sprintf($lang_signup{'add_fail'}, "$user\@$domain").$rc);
        } else {
            $tpl->assign(SUCCESS => sprintf($lang_signup{'add_ok'}, "$user\@$domain"));
            my $dir = $sys->{SYS_CONFIG};
            my $base = $sys->{SYS_MAILDIR_BASE};
            system("$dir/tools/maildirmake.pl $base/$path/Maildir/");
            $tpl->assign(STEP3 => 1);
            $self->toggle_user_status($cfm_info->[1], $cfm_info->[2], $cfm_info->[3]);
        }
    }
}

sub pre_run { 1 }

sub post_run {
    my $template = $_[0]->{query}->cgi('screen') || 'signup.html';
    $_[0]->{tpl}->process($template);
    $_[0]->{tpl}->print;
}

1;


syntax highlighted by Code2HTML, v. 0.9.1