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

I am having some problems with the following script. This is my first attempt at using an object oriented module, so there is a huge learning curve factor going on here.
This is what i would call Alpha code. There is a lot of error checking that I have not included. I am really looking for more experience getting used to the module.

The script take the input from a form, which consists of username, old password and new password; and then attempts to change the listed username from oldpw to newpw.

#!/usr/bin/perl -w use strict; use Apache::Htpasswd; my $htpasswd = "/etc/htsec/motion.ht"; my $method = new Apache::Htpasswd($htpasswd); my %formdata; my @pairs; my $buffer; my $pair; my @getpairs; my $key; my $value; &parseform(); if ($formdata{username}) { $method->htpasswd($formdata{username}, $formdata{newpw}, $formdata{o +ldpw}); } print <<HTML; Content-type: text/html\n\n <html> <head><title>Password Administration</title></head> <body bgcolor="#FFFFFF"> <form method = "post" action = "https://www.lunarmedia.net/motion/ch +pass.cgi"> Username:&nbsp;<input type="text" length="30" name="username"><br> Password:&nbsp;<input type="password" length="30" name="oldpw"><br +> New Password:&nbsp;<input type="password" length="30" name="newpw" +><br> <input type="submit" value="Go!"><br> </body> </html> HTML sub parseform { if ($ENV{'REQUEST_METHOD'} eq 'GET') { @pairs = split(/&/, $ENV{'QUERY_STRING'}); } elsif ($ENV{'REQUEST_METHOD'} eq 'POST') { read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs = split(/&/, $buffer); if ($ENV{'QUERY_STRING'}) { @getpairs =split(/&/, $ENV{'QUERY_STRING'}); push(@pairs,@getpairs); } } else { print "Content-type: text/html\n\n"; print "<P>Use Post or Get"; } foreach $pair (@pairs) { ($key, $value) = split (/=/, $pair); $key =~ tr/+/ /; $key =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1)) +/eg; $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1 +))/eg; $value =~ s/<!--(.|\n)*-->//g; if ($formdata{$key}) { $formdata{$key} .= ", $value"; } else { $formdata{$key} = $value; } } }

This is the uber bland code where I really just want to see the script change the password in the .htpasswd file listed. The htpasswd file has 644 perms with 'nobody' as the file owner (nobody being the account the webserver is running as)
I'm just starting to wield the perl sword with some skill, but this module business still has me back in the monastery asking master po for wisdom

humbly -c

Replies are listed 'Best First'.
Re: Apache::Htpasswd syntax questions
by thatguy (Parson) on Aug 04, 2001 at 08:34 UTC
    what's the problem you're having? also, i would suggest using CGI.pm as it will make the forms and mundane html easier.

    Update: Apache::Htpasswd is sweet! I had been writing htpasswd the old caveman way.. with crypt. This module makes things very nice.. oh and here's a little something that uses Apache::Htpasswd and CGI

    #!/usr/bin/perl -wT use CGI qw(standard); use CGI::Carp qw(fatalsToBrowser); use Apache::Htpasswd; my $htpasswd="htpasswd"; my $main=new CGI(); print $main->header; print $main->start_html('Password Administration'); $main->import_names('Q'); if ($Q::form) { if (($Q::username) && ($Q::oldpass) && ($Q::newpass)) { # all the forms have been entered &change_pass } else { print "please fill in all fields"; } } else { # must be a first time customer print $main->startform; print "<table border=0 cellpadding=1 cellspacing=0>\n"; print "<tr><td>Username: </td><td>",$main->textfield(-name=>'u +sername',-size=>30),"</td></tr>"; print "<tr><td>Password: </td><td>",$main->password_field(-nam +e=>'oldpass',-size=>30),"</td></tr>"; print "<tr><td>New Password: </td><td>",$main->password_field( +-name=>'newpass',-size=>30),"</td></tr>"; print "<tr><td>&nbsp;</td><td>",$main->submit(-name=>'form',-v +alue=>'Change Password'),"</td></tr></tab le>"; } sub change_pass { if (-e $htpasswd) { # if the htpasswd file exists my $htpass = new Apache::Htpasswd("$htpasswd"); # use Apache::Htpasswd's builtin function to verify old pass befor +e changing $htpass->htpasswd($Q::username,$Q::newpass,$Q::oldpass +); # get errors (if any) my $err=$htpass->error; if ($err) { print "Error. Most likely due to an inccorect +password or username \n"; } else { ## must be no errors print "user $Q::username password changed\n"; } } else { print "$htpasswd cannot be found\n"; } }

    -p

      Glad you like it! I think I (still) have a newline issue in the code somewhere but haven't had the tuits to fix it yet. If you have any problems with newlines, send me some example code (email in the POD) and it may prod me to find tuits :)

      Cheers,
      KM

Re: Apache::Htpasswd syntax questions
by tadman (Prior) on Aug 04, 2001 at 08:31 UTC
    The source code is great, but could you explain your problem again? I missed that part.

    Update:
    If you're having issues with run-time errors, which can result in the "500 Internal Error" messages, then you should do two things:
    1. use CGI::Carp so you can see them in your appropriate error_log
    2. Try and run the script stand-alone, like: % ./script.cgi ''
    This will make tracking down any source errors substantially easier.
      I'll look into CGI::Carp, however I've tried running it from the command line which is another source of frustration for me. I am getting

      Use of uninitialized value at ./chpass.cgi line 39. Use of uninitialized value at ./chpass.cgi line 41. Use of uninitialized value at ./chpass.cgi line 22.

      These lines correspond to the

      22: $method->htpasswd($formdata{username}, $formdata{newpw}, $formdata +{oldpw});

      and the lines

      if ($ENV{'REQUEST_METHOD'} eq 'GET') { @pairs = split(/&/, $ENV{'QUERY_STRING'}); } elsif ($ENV{'REQUEST_METHOD'} eq 'POST') {

      at first I thought that it was the subroutine, however when I comment out the if statement:

      if ($formdata{username}) { $method->htpasswd($formdata{username}, $formdata{newpw}, $formdata{o +ldpw}); }

      the html is printed out correctly and the script runs. I've tried some other test to see if the subroutine was parsing the form values correctly and it appears that it is. Which brings me back to thinking that the problem is in the way I have used the module syntax.

      humbly -c

      Update I worked this script a little further. I eyeballed thatguy's code and saw some extra spaces I had put into my $method->htpasswd statement. When I removed them, the code worked. However, I put them back in, and it still worked, which left me scratching my head why. Regardless, I've added some error checking to the code apart from the module's. I'm putting the code up at my home node since It would be a bit long for here.

      enjoy -c

Re: Apache::Htpasswd syntax questions
by chromatic (Archbishop) on Aug 05, 2001 at 00:27 UTC
    Using CGI.pm lets us drop the somewhat-broken parseform() altogether, making the entire program much shorter. Doing that also lets us have somewhat nicer error checking. The warnings you mention earlier have to do with form values not being present. Here's a potential fix:
    #!/usr/bin/perl -w use strict; use CGI; use Apache::Htpasswd; my $htpasswd = "/etc/htsec/motion.ht"; my $method = Apache::Htpasswd->new($htpasswd); my $q = CGI->new(); my %fields; for my $field (qw( username newpw oldpw )) { $fields{$field} = $q->param($field); unless (defined($fields{$field})) { showform(); exit(); } } $method->htpasswd(@fields{ qw(username newpw oldpw) }); # print a success message sub showform { my $q = shift; print $q->header(); print <<HTML; <html> <head><title>Password Administration</title></head> <body bgcolor="#FFFFFF"> <form method = "post" action = "https://www.lunarmedia.net/motion/chpa +ss.cgi"> Username:&nbsp;<input type="text" length="30" name="username"><br> Password:&nbsp;<input type="password" length="30" name="oldpw"><br> New Password:&nbsp;<input type="password" length="30" name="newpw"><br +> <input type="submit" value="Go!"><br> </body> </html> HTML }
    This allows us to verify that each field contains a defined value, aborting if necessary.

    Using CGI to generate the HTML would also give you the benefit of sticky form fields, where the contents of the field display on subsequent invocations.

    Update: Fixed two typos. The code ought to work now. Then a third, as c pointed out.