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

Hello everybody. Am very new to Perl and CGI, and now I desperately need help with two things: Am trying to write a script, in Perl, to store form data into a text file (personalDetails.dat) on a UNIX server. Have done so successfully, but need to modify it, and really cannot get my head around it. Here is the script:
#!/usr/bin/perl # Program to write data to a text file use CGI qw(:standard); use CGI::Carp(fatalsToBrowser); $firstname = param("firstname"); $lastname = param("lastname"); $email= param("email"); $country = param("country"); $username= param("username"); $password= param("password"); $rpassword = param("rpassword"); print "Content-type: text/html\n\n"; print "<html>\n"; print "<head>\n<title>CGI Script</title></head>\n"; print "<body>\n"; open(TEXTWRITE,">>personalDetails.dat")||die("Can't open personalDetai +ls.dat: $!"); print TEXTWRITE " $firstname"; print TEXTWRITE ","; print TEXTWRITE " $lastname"; print TEXTWRITE ","; print TEXTWRITE " $email"; print TEXTWRITE ","; print TEXTWRITE " $country "; print TEXTWRITE ","; print TEXTWRITE " $username"; print TEXTWRITE ","; print TEXTWRITE " $password"; print TEXTWRITE ","; print TEXTWRITE " $rpassword\n"; close(TEXTWRITE); print " Hello, $firstname $lastname.\n"; print "</body>\n</html>";
How do I modify it to check first, before it writes to the text file, if the username or password has already been registered by someone else? (Neither can be used more than once.) Second question is: I have a login page for excisting users. How do I modify script below to check through the entire text file when a username and password is entered? I think I need a loop at first if statement that caters for all possiblities(password do not match with username, etc) Here is the second script:
#!/usr/bin/perl use CGI qw(:standard); $testUsername = param("username"); $testPassword = param("password"); open (FILE, "password.txt") || die "File cannot be opened"; while($line = <FILE> ) { chomp $line; # removes carriage return ($username, $password) = split(", ", $line); if ($testUsername eq $username) { $userVerified = 1; if ($testPassword eq $password) { $passwordVerified = 1; last; # go to the last line }}} close (FILE); print "Content-type: text/html\n\n"; print "<head>\n<title></title></head>\n”; if ($userVerified &&$passwordVerified) { accessGranted(); } elsif ($userVerified && !$passwordVerified) { wrongPassword(); } else {accessDenied(); } sub accessGranted { print "Permission has been granted, $username."; } sub wrongPassword { print "You entered invalid password."; } sub accessDenied { print "You have been denied access to the server."; }
If anyone can help me I would be so thankful!! I am sorry this is so basic, and probably a silly question, but I have tried over and over again, and really need help to get it up and running!! Thanks again

Replies are listed 'Best First'.
Re: Perl to write and search text file
by Roy Johnson (Monsignor) on Jan 13, 2004 at 18:24 UTC
    Mixing Reads and Writes may be what you're looking for, although you could do two separate passes (one read, and one write). You want to read the file first and only write the information if the username is not found there. Your 2nd example includes most of the code for doing the reading, and your 1st example is code for doing the writing.

    For your 2nd example, you need to change the last to last if $userverified. As it is, you're immediately exiting the while loop on the first pass. You could change the comment, too, to exit the loop, which is a little more accurate.


    The PerlMonk tr/// Advocate
Re: Perl to wite and search text file
by Art_XIV (Hermit) on Jan 13, 2004 at 18:32 UTC

    Save yourself alot of hassles by checking out the DBD::CSV module or the docs for the DBM databases which come with Perl.

    DBD::CSV will let you use SQL queries and inserts against a flat-file, which should save you a nice chunk of work.

    Think 'join/split' if you want to try the DBM path, since it kind of lets you read and write a hash from/to disk. There are several DBM modules available, and the choice depends upon what is on your system. AnyDBM_File is a good place to start.

    If this is something that is meant to used for something other than experimentation, then after you implement one of the above then start thinking about using a relational database and your server's built-in authentication.

    Hanlon's Razor - "Never attribute to malice that which can be adequately explained by stupidity"
Re: Perl to wite and search text file
by sulfericacid (Deacon) on Jan 13, 2004 at 18:56 UTC
    I will have to agree with Art_XIV on this and suggest you look into some kind of database to store this amount of data for you; like it was already suggested, SDBM would work nice for a project like this.

    First and foremost, I don't see you using strict anywhere. When you're messing with this many variables, especially a username and password, it's safer if you use strict; in the beginning of your program. Please refer to strict or type perldoc strict in CMD.

    If you went with the SDBM route, you could check for already used usernames by using an if statement. Something like this is what I've always used (assuming the user name is used as the hash key):

    foreach (keys %personalDetails) # iterate over every key/value pair st +ored in database { if (exists $personalDetails{$testUsername}) # check to see if the us +ername exists using "exist" { print "Error: This screen name is already being used.\n"; exit; } }
    Using a database rather than writing to a file will make your life a lot easier. You won't have to use while (<>), checking to see if particular elements are already stored and retrieving data is a million times easier and faster.

    Another thing I noticed is you're adding leading spaces when you print to the personalData file. Are you sure you didn't want to put spaces at the end of the line instead?

    print TEXTWRITE " $firstname"; print TEXTWRITE ",";
    Could be shortened into one line, since it's always nicer being lazy, right?  print TEXTWRITE "$firstname,";

    One final thought. Since you're using CGI.pm, you can forget about printing HTML headers the old fashion way. Let CGI.pm do the work for you.  print header, start_html('This is the title of my page!'); prints all your header information for you in one line.

    I hope this helps.



    "Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

    sulfericacid
Re: Perl to wite and search text file
by blue_cowdawg (Monsignor) on Jan 13, 2004 at 19:00 UTC

    You should investigate Tie::File bro'!

    Here (stripped of all the HTML stuff) is an example of what you can do. To store your password entries you would do something like this:

    #!/usr/bin/perl -w use Tie::File; use strict; my @pwfile; tie @pwfile, 'Tie::File', 'pwfile.dat' or die $!; my $user = { name => 'Klem Kaddiddlehopper', uid => 'kkhopper', addr => '123 main street, anywhere, anystate, 12345, USA', passwd => 'shhh!' }; my @fields=qw / name uid addr passwd /; # # Add the record! push @pwfile,join('|',map{ $user->{$_}} @fields );
    and to read the entries you could do the following:
    #!/usr/bin/perl -w use Tie::File; use strict; my @pwfile; tie @pwfile, 'Tie::File', 'pwfile.dat' or die $!; # # Find a record my $uid='kkhopper'; my $found=0; foreach my $entry (@pwfile) { my @entries=split(/[\|]/,$entry); $found = 1 if ( $uid eq $entries[1]) and ($entries[3] eq 'shhh!' ); } printf "WHHHOOOPPIE!\n" if $found;

    I over simplified this for clarity sake.

    I leave implementing this as part of your CGI up to your imagination.


    Peter L. Berghold -- Unix Professional
    Peter at Berghold dot Net
       Dog trainer, dog agility exhibitor, brewer of fine Belgian style ales. Happiness is a warm, tired, contented dog curled up at your side and a good Belgian ale in your chalice.
Re: Perl to wite and search text file
by johndageek (Hermit) on Jan 13, 2004 at 18:40 UTC
    If you are looking for security, I strongly suggest using authentication via the server.

    Personally an implimentation of LDAP seems to give the most flexability, and will allow you to do the tests you are interested in. (You really do not care if the passcodes are unique, but you DO care that user names are unique).

    By the way, by letting someone know that the password they have requested is not unique across other users, you have created a security hole.

    Good luck!
    dageek