Re: CGI.pm 2.98 breaks CGI::Carp?
Drieux == Drieux [EMAIL PROTECTED] writes: Drieux just a quick knickle tour - I of course will defer to Randal Drieux to go into more detail if required. That covers most of it, but let me put it a different way. use CGI::Carp qw(fatalsToBrowser); presumes that the person operating the browser is the person also developing the program. While this can certainly be true during development, it is rarely true (if ever!) in production. Here's the problem: The end user doesn't want to see a Perl error message. They will probably have no clue what to do to work around it, and will rarely understand how to precisely copy that error (and any associated state or sequence descriptions) to a proper email buffer to email the developer. But worse, the end user *shouldn't* see the details of the error, which will often include filenames and other key data that might reveal the security mechanisms being used. This is prime intrusion vector information - a goldmine for the person wanting to abuse the website. All you should do for a non-recoverable error is tell the user: SOMETHING WENT WRONG. WE ALREADY KNOW ABOUT IT. IF YOU WANT TO TELL US WHAT YOU WERE DOING: email [EMAIL PROTECTED] referencing trouble ticket #12345. and then on the server side, log the hell out of the error, and create a trouble ticket so there can be a repair made and correlation with any user. If you don't want to create a ticket, just use unpack(H*, pack NS, time, $$), which should be fairly unique and yet short enough to cut and paste nicely. In fact, that'd be nice column idea! I needed one for TPJ today. Thanks. -- Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 [EMAIL PROTECTED] URL:http://www.stonehenge.com/merlyn/ Perl/Unix/security consulting, Technical writing, Comedy, etc. etc. See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training! -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: CGI.pm 2.98 breaks CGI::Carp?
Randall, Sorry, but what is TPJ ? Thanks, Nestor :-) -Original Message- From: Randal L. Schwartz [EMAIL PROTECTED] Sent: Sep 24, 2003 4:06 AM To: [EMAIL PROTECTED], Drieux [EMAIL PROTECTED] Subject: Re: CGI.pm 2.98 breaks CGI::Carp? Drieux == Drieux [EMAIL PROTECTED] writes: Drieux just a quick knickle tour - I of course will defer to Randal Drieux to go into more detail if required. That covers most of it, but let me put it a different way. use CGI::Carp qw(fatalsToBrowser); presumes that the person operating the browser is the person also developing the program. While this can certainly be true during development, it is rarely true (if ever!) in production. Here's the problem: The end user doesn't want to see a Perl error message. They will probably have no clue what to do to work around it, and will rarely understand how to precisely copy that error (and any associated state or sequence descriptions) to a proper email buffer to email the developer. But worse, the end user *shouldn't* see the details of the error, which will often include filenames and other key data that might reveal the security mechanisms being used. This is prime intrusion vector information - a goldmine for the person wanting to abuse the website. All you should do for a non-recoverable error is tell the user: SOMETHING WENT WRONG. WE ALREADY KNOW ABOUT IT. IF YOU WANT TO TELL US WHAT YOU WERE DOING: email [EMAIL PROTECTED] referencing trouble ticket #12345. and then on the server side, log the hell out of the error, and create a trouble ticket so there can be a repair made and correlation with any user. If you don't want to create a ticket, just use unpack(H*, pack NS, time, $$), which should be fairly unique and yet short enough to cut and paste nicely. In fact, that'd be nice column idea! I needed one for TPJ today. Thanks. -- Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 [EMAIL PROTECTED] URL:http://www.stonehenge.com/merlyn/ Perl/Unix/security consulting, Technical writing, Comedy, etc. etc. See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training! -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: CGI.pm 2.98 breaks CGI::Carp?
Nestor == Nestor Florez [EMAIL PROTECTED] writes: Nestor Randall, Nestor Sorry, but what is TPJ ? www.tpj.com - the Perl Journal, formerly a print quarterly, then bundled with SysAdmin, is now back as an online monthly. I have a column there, taken over from brian d foy, who is currently in Iraq under military orders until next April. -- Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 [EMAIL PROTECTED] URL:http://www.stonehenge.com/merlyn/ Perl/Unix/security consulting, Technical writing, Comedy, etc. etc. See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training! -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: CGI.pm 2.98 breaks CGI::Carp?
On Wednesday, Sep 24, 2003, at 09:23 US/Pacific, Randal L. Schwartz wrote: [..] www.tpj.com - the Perl Journal, formerly a print quarterly, then bundled with SysAdmin, is now back as an online monthly. I have a column there, taken over from brian d foy, who is currently in Iraq under military orders until next April. [..] p0: I look forward to the Article! p1: I think Nestor did two good things a. note a concern about the CGI::Carp technical bug b. raise the general problem of when is it time to 'unattach' from canonical CPAN Modules. And in particular surface the 'is the cgi ready for production' general issue. p2: There is a less well known feature in CGI::Carp that might have been addressed - as folks will note at the bottom of the 'perldoc CGI::Carp' - there is a 'set_message()' that COULD have been the fast work around for 'going to production': use CGI::Carp qw(fatalsToBrowser set_message); BEGIN { sub handle_errors { my $msg = shift; print h1Oh gosh/h1; print Got an error: $msg; } set_message(\handle_errors); } And we should all graciously apologize to Nestor if that was the Strategy that they had used; for clearly they had read the POD, and it was Good. note: the $msg is the 'die line' and could be 'fixed up' BEGIN { sub handle_errors { my $msg = shift; print h1Oh gosh/h1; $msg =~ s/at.*line\s*\d+.*$//; my $paragraph = p$msg/p; print \npGot an error:/p\np$msg/p\n; } set_message(\handle_errors); } This seems to be a good enough solution IF one is only writing a few simple CGI scripts - and for each one all of the possibilities are nice and neatly contained... But that way leads to having a 'common' BEGIN block for each piece of cgi code WAY TOO MUCH WORK. p3: But if One is Already in the process of building out a 'handle_error()' - why not simply park them where they are really needed and 'integrate' them into the flow of the general code and design pattern. Especially if one's CGI code has evolved into using modules as a core part of the process. To help illustrate this, I have a function get_from_server() that I built out of bits, and those bits use 'die()' in cases where it 'should just die'. So one wraps the call in an eval block, sets a local 'signal handler' for the '__DIE__' and checks how things went. our ($page,$h) ; our $wrapper_flag = 0; our $bytes_read = 0; eval { local $SIG{'__DIE__'} = sub { $wrapper_flag = 1;} ; ($bytes_read, $page, $h ) = get_from_server($host_port, $uri, $q); }; In this case I do not really need to know if $@ was set or not and return an 'error' to the caller if we died, or the stuff that is really useful. return({ runtime_error = Problems connecting to $host_port got status: $@ } ) if ( $wrapper_flag ); \$page; This way all of the cgi code that will come through this module has to deal with the fact that exception handling is going to be built in, and that they will be on their head what they do with the exception being passed back to them ciao drieux --- ok, so I liked Randall's general write up better, as he is a good writer... but as a Code Monkey well, sometimes more than one way of presenting the solution space may help FLOG THE PROBLEM -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Real beginner's questions
Hello, I just subscribed to this list today hoping I might find someone who could help me over some of my initial hurdles with learning to work with Perl and cgi- scripts. All I know about the subject I learned today in browsing the web, and I'm finding that all of the sites I go to assume a certain level of knowledge. Or else they say to ask my system or website administrator, which is very helpful since that is me! Even the archives of this mailing list are way over my head. I am in a small public library and we have our own webserver running Windows NT and IIS. I downloaded and installed active Perl (what's the difference between active Perl and regular Perl??) in the d:\perl directory on our server. The root of our webserver is at d:\website\extranet and I have created a cgi-bin directory under that. I'm not sure what else I need to do to get perl to work on our website. Also, we had an older version of perl installed at d:\website\perl by a previous administrator, and I'm not sure if there is going to be some kind of a conflict between the two. I need to learn how to get perl activated and working. Once that is done I can proceed on my own to learn how to write and edit scripts. Is there someone else out there who be interested in a bit of hand-holding today while I get this going? Bill Teschek Webmaster Lane Memorial Library 2 Academy Ave. Hampton, NH 03842 [EMAIL PROTECTED] (603)-926-3368 (603)-926-1348 (fax) http://www.hampton.lib.nh.us -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
guestbook mySQL / PERL
Helllo, I have some questions here about my code. (which is going to have some issues) I am going to send this as 2 emails so that you don't have to scroll endlessly :) Goal: display database in a table with checkboxes so that the user can delete selected messages Please let me know how I can Improve my script and if you see anything that is bad style/practice. Thanks, Dave Gilden - The display script #!/usr/bin/perl -w #display_guest.cgi use CGI qw/:standard/; use CGI::Carp qw(fatalsToBrowser); use DBI; use strict; my $user_name = ; my $user_password = ; my $sql_server = #; my $table_name = guestbook; my $qs = $ENV{'QUERY_STRING'}; my $secret_word = '###'; if ($qs !~ /$secret_word/) { print header; print Your not authorized to make changes!\n; exit; } my @colors = ('lightgrey', 'white'); my ($title,$sth,$dbh,$sql,$id,$maxEntries,%sort_direction,$offset,$sort_direction, $name,$email,$datecreated,$comments); initialize_dbi; # # get form data $sort_direction = param('display_direction'); $offset = param('offset'); # Get how many records there are total! run_statement(select * from guestbook;); $maxEntries= $sth-rows; init(lc(param('action'))); $sql = select * from $table_name order by id limit . $offset . ,10;; run_statement($sql); my @data_out; my $comments_num = $offset; my $firstVal = $comments_num; while (($id,$datecreated,$email,$name,$comments) = $sth-fetchrow) { $comments_num++; push(@data_out, tr\ntd . checkbox( -name ='id', -value =$id, -checked =0, -label='') . /tdtd$datecreated/tdtd$email/tdtd$name/tdtd$comments/td/tr\n); } $dbh-disconnect; # close date base connection $title = Guestbook entries $firstVal - $comments_num; ### # Start HTML OUT ### print header; print qq|meta http-equiv=Pragma content=no-cache\n|; print start_html( -title = $title ); print \n, h2($title); print start_form( -action = ./display_guestbook.cgi?$secret_word , -method = 'POST' ), \n; # # set hidden fields # param('offset', $offset); print hidden(-name = 'offset'), \np, ## # Buttons ## submit( -name= 'action', -value = 'Sort Display Order' ), nbsp;nbsp;\n; %sort_direction = ( 1 = 'First to Last ', 0 = 'Last to First ', ); print radio_group( -name= 'display_direction', -values = [ keys %sort_direction ], -default = $sort_direction, -labels = \%sort_direction ), /p\n; print nbsp;nbsp;\n; # space out the buttons print submit( -name = 'action', -value = 'Previous 10 Entries' ) if $offset 0; print nbsp;nbsp;\n; # space out the buttons print submit( -name = 'action', -value = 'Next 10 Entries' ) unless $comments_num == $maxEntries; print \n, end_form, \n; print brnbsp;br\n; print start_form( action = update_guestbook.cgi , -method = 'POST' ), \n; print submit( -name = 'delete', -value = 'Delete Selected' ); # Start Table Data print TABLEHEADER; brtable border=1 cellspacing=4 cellpadding=0 width=98% tr th style=color:red;Delete/ththDate Created/ththEmail/ththName/ththComments/th /tr TABLEHEADER # print $data_out; # Sort Direction for numbers # set up $comments_num to count up or down # $comments_num = ($sort_direction) ? 0 : 10; while (@data_out){ my $tmp = ($sort_direction) ? (shift @data_out) : (pop @data_out); print $tmp; } # End Table Rows print /table\n; print \n, end_form, \n ,end_html; print !--\$sort_direction: $sort_direction --\n; exit; ## # SUBS ## my $path_to_cgi = http://###/;; sub init{ my $act = shift; if ($act =~ /^sort/){ redirect($path_to_cgi/guestbook.cgi); } elsif ($act =~ m/^next/){ $offset += 10; } elsif ($act =~ m/^previous/) { $offset = $offset - 10; } else { $offset =0; $comments_num =0; $sort_direction =0; } } sub initialize_dbi{ my $drh = DBI-install_driver( 'mysql' ); $dbh = DBI-connect(DBI:mysql:$user_name:$sql_server, $user_name, $user_password); die Cannot connect: $DBI::errstr\n unless $ ; } sub run_statement{ my $stmt = $_[0]; $sth = $dbh-prepare($stmt); $sth-execute; } __END__ == Cora Connection: Your West African Music Source Resources, Recordings, Instruments More! http://www.coraconnection.com/ == -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
guestbook mySQL deleting and refreshing
Helllo, Here is part two: Goal: delete selected records and refresh display_guestbook.cgi page. So the users sees the changes made. Please let me know how I can Improve my script and if you see anything that is bad style/practice. Thanks in advance for the lists help this code, Regards from Ft. Worth, Dave -script to delete records from mySQL database #!/usr/bin/perl -w # update_guestbook.cgi use CGI qw/:standard/; use CGI::Carp qw(fatalsToBrowser); use DBI; use strict; my $user_name = ###; my $user_password = ###; my $sql_server = ; my $table_name = guestbook; my @guests_to_delete; my $path_to_cgi = ##; my ($sql,$dbh,$sth); my $secret_word = '###'; @guests_to_delete = param('id'); initialize_dbi; ### foreach (@guests_to_delete){ $sql = delete from $table_name where id =?; $sth = $dbh-prepare($sql); $sth-execute($_); } $dbh-disconnect; # close date base connection # cause guestbook page to be refreshed print redirect(./display_guestbook.cgi?$secret_word); exit; sub initialize_dbi{ my $drh = DBI-install_driver( 'mysql' ); $dbh = DBI-connect(DBI:mysql:$user_name:$sql_server, $user_name, $user_password); die Cannot connect: $DBI::errstr\n unless $ ; } __END__ == Cora Connection: Your West African Music Source Resources, Recordings, Instruments More! http://www.coraconnection.com/ == -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Real beginner's questions
On Wednesday, Sep 24, 2003, at 10:36 US/Pacific, Bill Teschek wrote: [..] I'm not sure what else I need to do to get perl to work on our website. Also, we had an older version of perl installed at d:\website\perl by a previous administrator, and I'm not sure if there is going to be some kind of a conflict between the two. I need to learn how to get perl activated and working. Once that is done I can proceed on my own to learn how to write and edit scripts. Is there someone else out there who be interested in a bit of hand-holding today while I get this going? [..] Bill, first things first - Active Perl is a Real Perl, the fine folks at activeState have done a lot of good work getting it to run in the Windows Environment. It sounds like you are totally new to both perl, coding, and web stuff. So my general references on the basic books you will want along the way is up at http://www.wetware.com/drieux/CS/Proj/TPFH/gen_doc.html Getting a feel for Perl itself is best done with something like the Learning Perl book. You will of course want something like the Learning Perl in WIN32 http://www.oreilly.com/catalog/lperlwin/ that should help you over the Perl Side of the problem. Which is the first part of your problem - how to feel at home with coding perl. The next part of the problem is understanding CGI. The Common Gateway Interface - this is mostly not that complex - since it is basically the 'things the web-server will pass to my code, and what I have to send back to it'. If you already understand perldoc perl that you can use to read perl's own 'online documentation' you will of course be able to specifically check out the documentation for the Perl Module CGI perldoc CGI This will of course help you into the real problem which is the HTML/XHTML standards debates - what sort of web page are you trying to present, and what really goes into 'web page design'. HTH. -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
RE: guestbook mySQL / PERL
David Gilden [EMAIL PROTECTED] wrote: : : Please let me know how I can Improve my script and if : you see anything that is bad style/practice. I just did a quick copy paste. Here are my initial reactions. You're using strict and warnings. Kudos. You are using white space and indentation but you are not using the same indentation throughout your program. You are using all your variables at file scope. Try to avoid this. Use the smallest scope possible and get out of the habit of declaring your variables all at the same time. declare them as you use them. Call subroutines without the '' prefix. Use initialize_dbi() instead of initialize_dbi. In fact it would be best to return the database handle: # line up like terms when appropriate my $user_name = ; my $user_password = ; my $sql_server= #; # return the database handle rather that setting a # global from within the sub my $dbh = initialize_dbi( $sql_server, $user_name, $user_password ) . . . sub initialize_dbi { my( $sql_server, $user_name, $user_password ) = @_; # I didn't know you had to this? # I haven't done DBI much though. my $drh = DBI-install_driver( 'mysql' ); my $dbh = DBI-connect(DBI:mysql:$user_name:$sql_server, $user_name, $user_password); # Do not return this error message in production code. die Cannot connect: $DBI::errstr unless $dbh; return $dbh; } We now know where $dbh came from. And where $sql_server, $user_name, and $user_password went to. In the sub we know what was passed in and what was passed out. In most functions you retrieve some data, process it, and return a result. The processing should not affect or be effected by an outside variable that was not passed in (except in the case of constants, persistent variables, and some rare occasions). Using global variables like you're doing *will* bite you down the line. Here's a conversion (I didn't test it) for run_statement(): # Get total records my $sth= run_statement( $dbh, select * from guestbook;); my $maxEntries = $sth-rows; # This may also work. You'll need to test it. my $maxEntries = run_statement( $dbh, select * from guestbook; )-rows; sub run_statement { my( $dbh, $statement ) = @_; my $sth = $dbh-prepare( $statement ); $sth-execute; return $sth; } Nothing in run_statement() is affected by or effects variables outside its scope other than through its return value. Stick to the same style for separating words in your variables and subs throughout the entire program. I prefer using the underscore: run_statement(), not RunStatement(). Whichever you choose, stick to it. And try not to abbreviate When you need help later on, it might be someone who doesn't use English as a first language trying to help. HTH, Charles K. Clarkson -- Head Bottle Washer, Clarkson Energy Homes, Inc. Mobile Home Specialists 254 968-8328 -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Real beginner's questions
On Wednesday, Sep 24, 2003, at 11:39 US/Pacific, Bill Teschek wrote: HTH, Thanks for responding. My questions at this point are more in line with how to get a script file to work with perl on our webserver. I'm been doing static HTML for years now but have just never ventured into cgi and perl stuff. that is clearly good news. Trying to make the Hop to HTML and cgi and perl all at once would drive one BATTY. Everyone starts somewheres and you have started in the right place. I'm hoping I can get some of the basics down right away to get a simple mailto form working on our site fairly quickly. Why Code when you can download: cf: http://nms-cgi.sourceforge.net/scripts.shtml specifically I would use the TFMail - as it is both GOOD perl code, and will give you an introduction to how 'not a simple thing' managing a 'mailto' form has to be - unless one wants every Spammer in the Free World using your web-site as an open relay... One question I have is about the first line of perl scripts: #! etc etc. I have not been able to find any explanation anywhere of how that works. Is that strictly a Unix thing, and if so, how does a MS server know where to find perl in order to run the script. [..] yes, that is known in the unix world, amongst other things as the 'SheBang' - it indicates to the 'shell' which interpretor should be invoked to read the rest of the text as 'code'. you might want to spin through http://aspn.activestate.com/ASPN/Perl/ { WOW! way too much stuff there at times, hum... } what you can do is run the perl.exe my_new_file.pl where you have put your perl CGI code into the file 'my_new_file.pl' and it should give you the output. Unix geeks would of course have done say perl ./my_new_file.plx but the basic idea is that anything that the Web Server is going to 'execute for you' you can basically do by hand at the command line. How the Web Server 'knows' which perl to invoke is a part of the configuration issues. In the Unix world with the #!/usr/bin/perl -w use strict; as the first too opening lines - all one has to do is put it in a file my_code.cgi - and the webserver 'knows' that anything ending in 'cgi' will simply be executed. I'll defer to someone with more IIS tuning time for the details on whether it is using the 'file name suffix' to invoke the perl processor on it - or which. HTH -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
HTML headers with CGI.pm
Hello, Having some issues with cgi.pm Here is my PERL: ### # Start HTML OUT ### print header; print start_html( -title = $title ); print qq|meta http-equiv=Pragma content=no-cache\n|; and here is the output: ?xml version=1.0 encoding=iso-8859-1? !DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd; html xmlns=http://www.w3.org/1999/xhtml; lang=en-US xml:lang=en-USheadtitleJake#39;s Guestbook entries 0 - 10/title /headbodymeta http-equiv=Pragma content=no-cache How do I move the Pragam inside the head and it also looks like I have two doctype definitions. Thanks for any suggestions. Dave Gilden PS: For something to listen while you code check out the BBC's Radio 3. Andy Kershaw treks across Mali to a desert music festival and back to Bamako, Mali. http://www.bbc.co.uk/radio3/world/desert.shtml Visit this page for CDs of some of the artists mentioned in this radio show: http://www.coraconnection.com/pages/other_cds.html --- -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: CGI.pm 2.98 breaks CGI::Carp?
Thanks -Original Message- From: Randal L. Schwartz [EMAIL PROTECTED] Sent: Sep 24, 2003 9:23 AM To: Nestor Florez [EMAIL PROTECTED] Cc: [EMAIL PROTECTED], Drieux [EMAIL PROTECTED] Subject: Re: CGI.pm 2.98 breaks CGI::Carp? Nestor == Nestor Florez [EMAIL PROTECTED] writes: Nestor Randall, Nestor Sorry, but what is TPJ ? www.tpj.com - the Perl Journal, formerly a print quarterly, then bundled with SysAdmin, is now back as an online monthly. I have a column there, taken over from brian d foy, who is currently in Iraq under military orders until next April. -- Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 [EMAIL PROTECTED] URL:http://www.stonehenge.com/merlyn/ Perl/Unix/security consulting, Technical writing, Comedy, etc. etc. See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training! -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: HTML headers with CGI.pm
David Gilden [EMAIL PROTECTED] wrote in message news:[EMAIL PROTECTED] Hello, Having some issues with cgi.pm Here is my PERL: ### # Start HTML OUT ### print header; print start_html( -title = $title ); print qq|meta http-equiv=Pragma content=no-cache\n|; and here is the output: ?xml version=1.0 encoding=iso-8859-1? !DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd; html xmlns=http://www.w3.org/1999/xhtml; lang=en-US xml:lang=en-USheadtitleJake#39;s Guestbook entries 0 - 10/title /headbodymeta http-equiv=Pragma content=no-cache First you print the output of the start_html() method, and then a html tag named 'meta'. Its doing exactly what you told it to. How do I move the Pragam inside the head and it also looks like I have two doctype definitions. Thanks for any suggestions. You do not have two doctype definitions. Read the specs on dtds. As far as the meta tag, try reading the CGI.pm documentation: perldoc -m CGI heres an excerpt: print $query-start_html(-title='Secrets of the Pyramids', -author='[EMAIL PROTECTED]', -base='true', -target='_blank', -meta={'keywords'='pharaoh secret mummy', 'copyright'='copyright 1996 King Tut'}, -style={'src'='/styles/style1.css'}, -BGCOLOR='blue'); After running this your next question will be how to create a http-equiv meta tag. Again, read the docs. Todd W. -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]