http://www.perlmonks.org?node_id=279710

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

This is a really wierd one. I have come across an issue where issuing a chop command in a subrotine called before I set a cookie using CGI.pm prevents that cookie from being set.

Yeah, I know. It sounds totally strange. But it's the only explanation I can find, and replacing my chop with a simple substr eliminates the problem!

There are no errors with that statement in the error logs, and execution of the script continues unabated, so I know that's not the problem. Has anyone else run into this? What could this be due to?

Here's a bit of code:
if (($user)&&($passcheck)) { # create a MD5 hash for session management my $session_brand = md5_hex(rand()); (my $session_timestamp, my $session_expiration) = get_timestam +p(); my $session_done = set_session($session_brand,$session_timesta +mp, $session_expiration); my $cookie = cookie(-domain=>"$meta_configs{xina_domain}", -name=>"$meta_configs{login_cookie_name}", -value=>"$session_brand", -path=>"$meta_configs{login_cookie_path}", -expires=>"$meta_configs{login_cookie_expi +ration}"); print $q->header(-cookie=>[$cookie]); # cookie set
The culprit subroutine was get_timestamp() - here it is:
sub get_timestamp { (my $year, my $month, my $day, my $hour, my $min, my $sec) = Today +_and_Now(); my $now_ts = "$year-$month-$day $hour:$min:$sec"; my$Dd; my $Dh; my $Dm; # cookie expiration is always in the form 10m or 1d my $time_frame = chop($meta_configs{login_cookie_expiration}); if (($meta_configs{login_cookie_expiration} =~ /[0-9]+/)&&($time_f +rame eq "m")) {$Dm = $meta_configs{login_cookie_expiration};} elsif (($meta_configs{login_cookie_expiration} =~ /[0-9]+/)&&($tim +e_frame eq "h")) {$Dh = $meta_configs{login_cookie_expiration};} elsif (($meta_configs{login_cookie_expiration} =~ /[0-9]+/)&&($tim +e_frame eq "d")) {$Dd = $meta_configs{login_cookie_expiration};} else {$Dh = 1;} # make a default expiration of 1 hour (my $nyr, my $nmo, my $nday, my $nhr, my $nmin, my $nsec) = Add_De +lta_DHMS($year, $month, $day, $hour, $min, $sec,$Dd, $Dh, $Dm, 0); my $expiration = "$nyr-$nmo-$nday $nhr:$nmin:$nsec"; return ($now_ts, $expiration); }
The culprit line is:  my $time_frame = chop($meta_configs{login_cookie_expiration});
If I replace that with: my $time_frame = substr ($meta_configs{login_cookie_expiration},-1,1); Everything works fine!

Thanks for any ideas. I'm not stuck, I obvioulsy solved the problem, but it seems strange, and I'd love to find out what the issue really is.

Replies are listed 'Best First'.
Re: chop and CGI.pm cookie problem?
by antirice (Priest) on Jul 31, 2003 at 18:51 UTC

    You're right. It should cause problems. However, the reason why chop is causing a problem is that you are thrashing $meta_configs{login_cookie_expiration} so that it is missing the last character. In other words, your expires option is invalid. Check out CGI for more info on what is acceptable for the expires field of the cookie. If you were to follow the my $time_frame = chop($meta_configs{login_cookie_expiration}); line with $meta_configs{login_cookie_expiration}) .= $time_frame; it should work as expected.

    Hope this helps.

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

      Um, I'm missing something here. $meta_configs{login_cookie_expiration} is going to hold something like 15m, or 1d, or 3h.
      So if I chop that, I should get: $time_frame = "d" or "h" or "m"
      and $meta_configs{login_cookie_expiration} = some_numeric_value that I use to figure out expiration.
      I never use that variable again after this routine, so destroying it is of no consequence. And, if I do the following:
      my $exp = $meta_configs{login_cookie_expiration}; my $time_frame = chop($exp);

      (and replace $exp in the place of the meta_config variable in the later lines), I get the same problem.
      Also, the default (if, for some reason, the meta_config variable is kerfluxed) is to set an expiration date of one hour, and return to the main routine, so worst case scenario should simply be 1 hour expirations for the session, not no cookies set.

        ...but it IS of consequence if you thrash $meta_configs{login_cookie_expiration} because you use it two lines after get_timestamp is called.

        If you replaced everything within get_timestamp in that way, it should work. The problem is that if you set -expires to 1 (after being chopped from 1h) then the cookie expires immediately. If you would like to see what exactly is going on try connecting to your webserver on port 80 and type in a valid request for the script and see what the head says. If the cookie that the page returns says that it expires Thu, 01-Jan-1970 00:00:01 GMT, then you are using an invalid expiration representation in $meta_configs{login_cookie_expiration} (which is the variable you use for expires). Try printing $meta_configs{login_cookie_expiration} to the page's output. If it doesn't end with s,m,h,d,M, or y, isn't "now", and isn't a fully qualified date, then CGI happily sets the expires to Thu, 01-Jan-1970 00:00:01 GMT.

        If you could post the new version of get_timestamp as well as what $meta_configs{login_cookie_expiration} contains, we'd be more than happy to help.

        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: chop and CGI.pm cookie problem?
by RMGir (Prior) on Jul 31, 2003 at 19:13 UTC
    chop is destructive, substr isn't.

    chop _removes_ the last character from $meta_config{login_cookie_expiration} and returns it (setting $time_frame). substr just returns the last character, without modifying the $meta_config entry.

    Obviously, in this case, changing the hash entry is bad. It would get "badder" if you called the routine several times, as you nibbled away at the string character by character.
    --
    Mike

Re: chop and CGI.pm cookie problem?
by skyknight (Hermit) on Jul 31, 2003 at 18:51 UTC

    My eyes are veritably crossing and dilating trying to look at your get_timestamp function... Wouldn't everything be incredibly simpler if you did it all with seconds, storing times as seconds since the epoch, durations as periods of seconds, and just performing a simple mathematical subtraction to figure out whether something had expired? Maybe I'm missing something, but I think you're passing up an opportunity to do something simple simply, giving rise, as you've noticed, to more opportunities for code defects.

      It might be easier if I hadn't gotten so used to using Date::Calc to calculate dates (for a different project), so that's my habit.