# 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.
#
# Extmail - a high-performance webmail to maildir
# $Id$
use strict;
use DBI;
package Ext::Auth::MySQL;
use Exporter;
use Ext::Passwd;
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(auth);
sub new {
my $this = shift;
my $self = bless {@_}, ref $this || $this;
$self->init(@_);
$self;
}
sub init {
my $self = shift;
my %opt = @_;
$opt{host} = '127.0.0.1' if not defined $opt{host};
$opt{dbname} = 'extmail_db' if not defined $opt{dbname};
$opt{dbuser} = 'root' if not defined $opt{dbuser};
$opt{dbpw} = 'password' if not defined $opt{dbpw};
$self->{opt}=\%opt;
my $connect = "DBI:mysql:database=$opt{dbname};host=$opt{host}";
if ($opt{socket}) {
$connect .= ";mysql_socket=$opt{socket}";
}
my $dbh = DBI->connect(
$connect,$opt{dbuser}, $opt{dbpw}, {'RaiseError' => 1}
);
$self->{dbh} = $dbh;
$self->{pwhandle} = Ext::Passwd->new(
fallback_scheme => $opt{crypt_type} || 'crypt'
); # default type
}
# XXX meaningful for MySQL driver only
sub build_sql {
my $self = shift;
my $schema = $self->{opt}->{'schema'};
my @params = @_;
my $username = $self->{opt}->{'table_attr_username'};
my $sql;
if ($schema eq 'vpopmail1') {
my $domain = $self->{opt}->{'table_attr_domain'};
$sql = "SELECT * FROM $self->{opt}->{table} WHERE ";
$sql .= "$username='$params[0]' AND $domain='$params[1]'";
} elsif ($schema eq 'vpopmail2') {
my $table_name = $params[1];
$table_name =~ s![-\.]!_!g;
$sql = "SELECT * FROM $table_name WHERE ";
$sql .= "$username='$params[0]'";
} else {
$sql = "SELECT * FROM $self->{opt}->{table} WHERE $username='$params[0]'";
}
$sql;
}
sub search {
my $self = shift;
my %res = ();
my $username = $self->{opt}->{'table_attr_username'};
my $SQL = $self->build_sql(@_);
my $sth = $self->{dbh}->prepare($SQL);
$sth->execute();
while(my $r=$sth->fetchrow_hashref()) {
$res{$r->{$username}} = $r; # feedback all rows
};
$sth->finish();
\%res; # return a REF
}
# return value redifination since 0.24-RC2
#
# $rv = 0 LOGIN_OK
# $rv = -1 LOGIN_FAIL
# $rv = 1 LOGIN_DISABLED
# $rv = 2 LOGIN_DEACTIVE
# $rv = 3 LOGIN_EXPIRED
sub auth {
my $self = shift;
my ($username, $password) = (@_);
my $res;
my $schema = $self->{opt}->{'schema'};
if ($schema =~ m!^vpopmail!) {
$username =~ /([^\@]+)@([^\@]+)/;
$res = $self->search($1, $2);
$username = $1; # XXX vpopmail
} else {
$res = $self->search($username); # XXX virtual
}
if(scalar keys %$res) {
my $pwd = $res->{$username}->{$self->{opt}->{'table_attr_passwd'}};
my $rv = -1; # flag to indicate authentication fail/ok/disabled
my $handle = $self->{pwhandle}; # Ext::Passwd object
# this step is a must, or null userpassword record will cause hole
# that anonymous can step in the system
return -1 unless($password && $pwd);
if ($handle->verify($password, $pwd)) {
if ($self->{opt}->{'table_attr_disablewebmail'} &&
$res->{$username}->{$self->{opt}->{'table_attr_disablewebmail'}}) {
return ($rv = 1);
}
if ($self->{opt}->{'table_attr_active'} &&
!$res->{$username}->{$self->{opt}->{'table_attr_active'}}) {
return ($rv = 2);
}
$self->{INFO} = $self->_fill_user_info($res->{$username});
return 0;
}else {
return -1;
}
}
-1; # default to fail
}
sub change_passwd {
my $self = shift;
my ($username, $old, $new) = @_;
# verify old password
if($self->auth($username, $old) == 0) {
# encrypt new password and update it
my $handle = $self->{pwhandle};
my $type = $handle->{_scheme};
my $crypted_new = $handle->encrypt($type, $new);
my $table = $self->{opt}->{table};
my $schema = $self->{opt}->{'schema'};
if ($schema =~ /^vpopmail/) {
$username =~ /([^\@]+)@([^\@]+)/;
$username = $1; # XXX vpopmail style
if ($schema eq 'vpopmail2') {
$table = $2;
$table =~ s![-\.]!_!g;
}
}
my $attr_pw = $self->{opt}->{table_attr_passwd};
my $clearpw = $self->{opt}->{table_attr_clearpw};
my $attr_un = $self->{opt}->{table_attr_username};
my $SQL = "UPDATE $table set $attr_pw='$crypted_new'";
if ($clearpw) {
$SQL .= ",$clearpw='$new'";
}
$SQL .= " WHERE $attr_un='$username'";
my $sth = $self->{dbh}->prepare($SQL);
$sth->execute;
$sth->finish();
return 1;
}else {
return 0;
}
}
sub _fill_user_info {
my $self = shift;
my $opt = $self->{opt};
my $entry = $_[0];
my %info = ();
# original infomation filling
foreach my $key (keys %$entry) {
$info{$key} = $entry->{$key};
}
# compatible with ExtMail ldap version
$info{QUOTA} = $info{$opt->{'table_attr_quota'}};
$info{NETDISKQUOTA} = $info{$opt->{'table_attr_netdiskquota'}};
$info{HOME} = $info{$opt->{'table_attr_home'}}; # must exists
$info{MAILDIR} = $info{$opt->{'table_attr_maildir'}} || "$info{HOME}/Maildir";
if ($info{$opt->{'table_attr_disablenetdisk'}}) {
$info{OPTIONS} = 'disablenetdisk';
}
if ($info{$opt->{'table_attr_disablepwdchange'}}) {
$info{OPTIONS} = ($info{OPTIONS} ? $info{OPTIONS}.',' : '') .'disablepwdchange';
}
\%info;
}
sub DESTORY {
my $self = shift;
$self->{dbh}->disconnect();
undef $self;
}
1;
syntax highlighted by Code2HTML, v. 0.9.1