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

I've now learned far more about cookies than I ever wanted to know, but I still haven't been able to solve my problem after several days of exhaustive research and effort.

My application is attempting to store the name of an individual when they complete an application, but it appears that the :
print "Content-type: text/html\n\n";
is causing my problem. The cookie data appears on the screen above the confirmation message after the application is submitted.

I've read that the cookie must be written before the "content" line is encountered, but how does one do that if they must first display a screen to collect the data? Is there a way to negate the effect of the "content" line until data must again be displayed?

I've moved the "Set-cookie:" command to a separate script, accessed as a subroutine after a "require" command is issued, but the "content" status persists.

I hope that I've made the problem clear. During the past few days I've tried everything I can think of to resolve this problem, and failed. Hopefully, it's something simple that I've overlooked.

Here's the code that calls the subroutine :
require "common.cgi"; write_cookie;
And here's the "common.cgi" script:
#!/usr/bin/perl # sub write_cookie { $wc_namef = $INPT{'reg_custnamef'}; $wc_namemi = $INPT{'reg_custnamemi'}; $wc_namel = $INPT{'reg_custnamel'}; print "Set-Cookie: b_first=$wc_namef; path=/; expires=Wed, 30-Dec- +2020 00:00:00 GMT;"; print "Set-Cookie: b_mid=$wc_namemi; path=/; expires=Wed, 30-Dec-2 +020 00:00:00 GMT;"; print "Set-Cookie: b_last=$wc_namel; path=/; expires=Wed, 30-Dec-2 +020 00:00:00 GMT;"; } 1;
Perhaps most frustrating of all is the knowledge that at some point during trial and error testing, I affected the existing cookie record for the site, but I must have been asleep at the switch because I don't know which of the hundreds of attempts worked, nor have I been able to duplicate it. :-)

Replies are listed 'Best First'.
Re: Cookies write to screen, not to cookie file
by jeffa (Bishop) on Jul 01, 2004 at 18:43 UTC
      As I mentioned, I've been working on this one problem for days. I've tried to use CGI::Cookie, copying the example directly from the CPAN page. I didn't care what data I wrote to the cookie file file, as long as I wrote something. Here's the script I used:
      #!/usr/bin/perl #################################### use CGI qw/:standard/; use CGI::Cookie; sub write_cookie { my $c = new CGI::Cookie(-name => 'foo', -value => ['bar','baz'], -expires => '+3M' ); print header(-cookie=>[$c]); %cookies = fetch CGI::Cookie; $id = $cookies{'ID'}->value; %cookies = parse CGI::Cookie($ENV{COOKIE}); } 1;
      This is what displays on the screen:

      Set-Cookie: foo=bar&baz; path=/; expires=Wed, 29-Sep-2004 19:22:16 GMT Date: Thu, 01 Jul 2004 19:22:16 GMT Content-Type: text/html; charset=ISO-8859-1

      The rest of the page is blank. Since this was even worse that what I was getting before, I returned to work with my former code.

      I also tried CGI, using an example I was able to find:
      #!/usr/bin/perl #################################### use CGI; $cgiobject=new CGI; $cgiobject->use_named_parameters; &get_state_variables; $cookie_data=&prepare_cookie; &set_cookie($cookie_data); return; sub get_state_variables() #retrieve from the CGI queries the keys and +value we want to store in the cookie { $b_first=$cgiobject->param("reg_custnamef"); $b_mid=$cgiobject->param("reg_custnamemi"); $b_last=$cgiobject->param("reg_custnamel"); } sub prepare_cookie() #packages the variables into one data string +for storage in cookie { $cookie_data="b_first=$b_first|". "b_mid=$b_mid|". "b_last=$b_last"; return $cookie_data; } sub set_cookie($cookie_data) #sets cookie on user's machine { $final_cookie=$cgiobject->cookie(-name=>'searchform', -value=>$cookie_data, -expires=>'+6M'); print $cgiobject->header(-cookie=>$final_cookie); } 1;
      but I couldn't get it to run. I tried using SYSTEM and then EXEC, neither of which I've ever tried using before, but to no avail.

        Uh...try this?

        common.cgi

        #!/usr/bin/perl #################################### use CGI qw/:standard/; use CGI::Cookie; sub write_cookie { my $c = new CGI::Cookie(-name => 'foo', -value => ['bar','baz'], -expires => '+3M' ); print header(-cookie=>[$c]); %cookies = fetch CGI::Cookie; ## This is very bad. You should check that $cookies{'ID'} exists ## or your script will die when you run this code. # $id = $cookies{'ID'}->value; ## You've already filled %cookies with the cookies. Why ## do it again? # %cookies = parse CGI::Cookie($ENV{COOKIE}); } 1;

        script.pl

        #!/usr/bin/perl require "common.cgi"; write_cookie(); print "Something";

        antirice    
        The first rule of Perl club is - use Perl
        The
        ith rule of Perl club is - follow rule i - 1 for i > 1

Re: Cookies write to screen, not to cookie file
by sgifford (Prior) on Jul 01, 2004 at 18:54 UTC
    As jeffa suggests, there are modules for doing this. But really, all you should have to do is print the cookie-setting headers before the blank line after the Content-Type header. You imply that this is tricky to do in your particular script, but I don't understand your explanation. A cookie is sent as part of a page; it doesn't make any sense to say you need to collect data before you can send the cookie for that page. Of course you can collect data and set a cookie from the resulting page, but you just have to set the cookie in the headers of that page.

    Also, your print statements with the cookies are missing \r\n at the end.

      I've tried to use the modules, without success. My own ignorance is undoubtedly responsible. I'm still trying to learn Perl, after several years of writing scripts, and I haven't studied the modules.

      I tried to limit the amount of information that I included in my original problem declaration because I know that much of it would be superfluous, but here's a little background.

      The application is a shopping cart system. The main script, named kart.cgi, does contain a  content command line. Most of the output takes place in another script, called config.cgi. The latest script is called register.cgi, and contains no  print statements. All information is passed to config for output.

      All of the subroutines for registration are in the register.cgi script, but control remains with kart.cgi. I thought that by using the  require command, the  content would be negated when the subroutines were accessed, but apparently not. :-)

      Until the visitor inputs the information that I am trying to output with the cookie, how can I write it to his or her cookie file? Ergo, I need to collect the data before I can write it out. If I was simply creating a visit count, or something like that, I could write the cookie as soon as the kart.cgi is opened.

      To the best of my knowledge, the \r\n at the end of a print statement is only important as a delimiter when viewing source code. Is there another reason why it should be in the  Set-cookie: command line?
        I need to collect the data before I can write it out.

        Yes, that is true. However, this means that the user will have to click a link that sends the information or submit a form that sends the information before you can set a cookie.

        You need some logic in your script that can detect whether the user already has a cookie, has just submitted data that you can use to set a cookie, or has no cookie or cookie-making data. Only then can you solve this problem.

        Also keep in mind that you cannot set a cookie and read its values in the same invocation. I think this is what's confusing you most right here. CGI programming is rather like a conversation between two very polite people who never interrupt and wait for a complete response before answering back.

        As chromatic says, you need to display the form as one Web page, then submit it to a script that will output the cookie headers. That script will need to parse the information the user entered, then output the headers (including the cookie headers), then output the body of the Web page.

        As for line endings in the headers, they are important, though whether you should use "\n" or "\r\n" may be Web server dependent (I usually use "\n", and assume my Web server will change it to a protocol-appropriate line ending). Line endings are how the Web server and browser know that one header has ended and the next has begun.

Re: Cookies write to screen, not to cookie file
by tachyon (Chancellor) on Jul 02, 2004 at 00:04 UTC

    You need to understand how cookies and HTTP headers work. The headers contain the cookie and *should* look like:

    HTTP/1.1 200 OK Set-Cookie: blah [other headers] Content-Type: text/html <-- Blank line ie \n\n after text/html term +inates header [Form Content Here]

    If you see the Cookie as plaintext you have outputted a valid \n\n terminated header *before* you have outputted the Set-Cookie Header. Unbuffer with $|++; at the top of the script, and check the output order. Go to http://web-sniffer.net/ and test your script to see what is being sent in what order. You are getting the order wrong.

    cheers

    tachyon

Re: Cookies write to screen, not to cookie file
by tadamec (Beadle) on Jul 01, 2004 at 20:13 UTC

    As others have said, CGI and CGI::Cookie are perfect for this.

    It sounds suspiciously like a buffered-output problem, where one module is buffering the output to be sent to STDOUT on completion of the script, and you're subverting the purity of the HTTP headers by issuing a print statement before it's time.

    It also could be, as others have said, that you're not placing the "\r\n" after the cookie header.

    Unless you're writing your own one-off modules to learn more about the CGI interface, in general, please, please, please use the various CGI modules; they're well tested and make writing secure CGI scripts if not simple, then at least attainable for a novice.

Re: Cookies write to screen, not to cookie file
by heroin_bob (Sexton) on Jul 01, 2004 at 18:59 UTC
    I agree, CGI & CGI::Cookie will definitely make it easier.
    ~hb