biosysadmin has asked for the wisdom of the Perl Monks concerning the following question:

Howdy monks. I'm writing an application for a web and e-mail hosting company that will allow customers to change the e-mail settings for their domain. I've come up with some code, and I'd like to submit it here for general comments. Also, I'd obviously like to add some form of authentication/authorization to the application, and I'd really appreciate some suggestions for appropriate modules to use.

In typical CGI::Application fashion, I've created a module in which most of the real work is done. Here's the code for that module.:

#!/usr/bin/perl -w package MailAdmin; use strict; use DBI; use Pod::Usage; use HTML::Template; use base 'CGI::Application'; # constants my $TITLE = 'Biosysadmin.com E-mail Configuration'; my $dbh = &get_dbh; my $template = HTML::Template->new( filename => 'mail.tmpl' ); # set up everything correctly sub setup { my $self = shift; $self->start_mode( 'mode1' ); $self->run_modes( 'mode1' => 'print_aliases', 'mode2' => 'add_remote_alias', 'mode3' => 'add_local_alias' ); $self->param( 'dbh' => $dbh ); $self->param( 'template' => $template ); } # shut down everything after application exits sub teardown { my $self = shift; $self->param('dbh')->disconnect; } sub print_aliases { my $self = shift; # get the CGI query object my $q = $self->query(); my $domain; if ( $q->param( 'domain' ) ) { $domain = $q->param( 'domain' ); $template->param( 'DOMAIN' => $domain ); $template->param( 'TABLE' => 1 ); } else { $template->param( 'TABLE' => 0 ); } # get a list of the local aliases my %aliases = %{ get_domain_addresses( $domain ) }; my @loop_data; $tmpl_hash{'ADDRESS'} = $address; push @loop_data, \%tmpl_hash; } $template->param( 'ALIASES' => \@loop_data ); $template->param( 'TITLE' => $TITLE ); # form a list of printed domains my @domains = @{ &get_valid_domains }; my @form_loop; foreach my $domain ( @domains ) { my %tmpl_hash; $tmpl_hash{'DOMAIN'} = $domain; push @form_loop, \%tmpl_hash; } $template->param( 'DOMAINS' => \@form_loop ); # develop the printed output my $output = $template->output; return $output; } sub not_implemented { my $output = "<h1>Sorry, this function isn't implemented yet</h1>"; $output .= '<a href="mail.cgi">Return home</a>'; return $output; } sub add_remote_alias { my $output = &not_implemented; return $output; } sub add_local_alias { my $output = &not_implemented; return $output; } ################### # DBI subroutines # ################### sub add_alias { my ($user,$alias) = shift; print "Delivering mail to $alias for user $user ...\n"; my $uid = uid( $user ); my $gid = gid( $user ); my $sql; $sql = "INSERT INTO aliases (vuid,vgid,alias,maildir) "; $sql .= "VALUES (?,?,?,?);"; my $sth = $dbh->prepare( $sql ) or die $dbh->errstr; $sth->execute( $uid,$gid,$alias,$user ) or warn $sth->errstr; if ($@) { print "Error executing SQL statement!\n"; } } sub get_valid_domains { my($sql, $domain); $sql = "SELECT domain FROM transport"; my $sth = $dbh->prepare( $sql ) or die $dbh->errstr; $sth->execute(); $sth->bind_columns( \($domain) ); my @domains; while ( $sth->fetch ) { push @domains, $domain; } return \@domains; } sub get_local_aliases { my $user = shift; # local aliases my ($vuid,$vgid,$address,$maildir); my $sql = 'SELECT vuid,vgid,alias,maildir FROM aliases'; $sql .= " WHERE maildir='$user'"; my $sth = $dbh->prepare( $sql ) or die $dbh->errstr; $sth->execute() or warn $sth->errstr; $sth->bind_columns( \($vuid,$vgid,$address,$maildir) ); my @addresses; while ( $sth->fetch ) { push @addresses, $address; } return \@addresses;sub get_domain_addresses { my $domain = shift; # get the remote aliases my ($alias,$rcpt,$sql); $sql = 'SELECT alias,rcpt FROM remote_aliases '; $sql .= "WHERE alias LIKE \"%\@$domain\""; my $sth = $dbh->prepare( $sql ) or die $dbh->errstr; $sth->execute() or warn $sth->errstr; $sth->bind_columns( \($alias, $rcpt) ); my %addresses; while( $sth->fetch ) { $addresses{ $alias } = $rcpt; } # get the local aliases my ($vuid,$vgid,$address,$maildir); $sql = 'SELECT vuid,vgid,alias,maildir FROM aliases '; $sql .= "WHERE alias LIKE \"%\@$domain\""; $sth = $dbh->prepare( $sql ) or die $dbh->errstr; $sth->execute() or warn $sth->errstr; $sth->bind_columns( \($vuid,$vgid,$address,$maildir) ); while ( $sth->fetch ) { $addresses{ $address } = $maildir; } return \%addresses; } sub get_dbh { my $db_user = 'user'; my $db_pass = 'password'; my $db_table = 'table'; my $dsn = "DBI:mysql:$db_table"; my $dbh = DBI->connect( $dsn, $db_user, $db_pass ) or die "Error connecting to database\n"; return $dbh; } sub uid { my $username = shift; my $retval = `id -u $username`; print $retval; if ( $retval eq '' ) { warn "Error obtaining gid information for $username\n"; } else { return $retval; } } sub gid { my $username = shift; my $retval = `id -g $username`; if ( $retval eq '' ) { warn "Error obtaining gid information for $username\n"; } else { return $retval; } } 1

Any kind of constructive criticism is greatly appreciated. Thanks. :)

  • Comment on CGI::Application Help: General Comments and Authentication Suggestions
  • Download Code

Replies are listed 'Best First'.
Re: CGI::Application Help: General Comments and Authentication Suggestions
by Belgarion (Chaplain) on May 01, 2004 at 17:27 UTC

    I wouldn't recommend naming your run-modes mode1, mode2, mode3, etc. However, that's just my personal preference. I would find it hard to remember what run modes I'm using, and what each run mode's "tag" was without referring to the setup function all the time.

    Another thing to look into is using the load_tmpl method already supplied by CGI::Application. It already creates a HTML::Template object. It would also be immediately understable to other CGI::Application users. In addition, you could overrride the default load_tmpl method and have it always define your common parameters, which would save you from having to do it in each run-mode.

    For your database section, I would look into Class::DBI or Ima::DBI. These are not strictly required for your present script, but they do move the SQL statements out of your script and into another module. I've had great success with Class::DBI personally.

      Thanks for the input. I'll probably move towards either Class::DBI or Ima::DBI soon, I'm a dinosaur who's been using pure DBI for so long it's always the first thing that pops into my head.

      I'll look into using the load_tmpl method I missed it in my cursory read of the documentation. Also, the mode1, mode2, ... thing I totally stole from the documentation, I'll definitely rename my run_modes more something more reasonable.

      Thanks for the help, I really appreciate it. :)

Re: CGI::Application Help: General Comments and Authentication Suggestions
by PodMaster (Abbot) on May 02, 2004 at 04:55 UTC
      Awesome links. I just started reading about Maypole tonight using this article on Perl.com. It looks really cool, but I think I'll have to apply myself over the next few weeks to really learn how to use it. I just finished installing Maypole on my server at work, so hopefully I'll get up to speed soon.

      Thanks a bunch for the help. :)