Cookies

A lot of webservers use client-side cookies for logging in. Here is some sample code of how to log in to an OpenACS 3.x server:

Login

 package require http
 set login [::http::formatQuery email [email protected] password fooFoo!]

 set tok [::http::geturl http://mysite.net/register/user-login.tcl -query $login]
 upvar \#0 $tok state
 set cookies [list]
 foreach {name value} $state(meta) {
     if { $name eq "Set-Cookie" } {
         lappend cookies [lindex [split $value {;}] 0]
     }
 }
 ::http::cleanup $tok

Using it

 set tok2 [::http::geturl http://mysite.net/some/restricted_page.html -headers [list Cookie [join $cookies {;}]]]
 ... your code
 ::http::cleanup $tok2

This method will work for a lot of websites that ask for a username and password. For each site, you'll probably want to read the page source of the login page of that site to see where it POSTs to and what fields you need to supply. And you may need to play with your User-Agent headers too. A lot of sites actually restrict which browsers can login...


male 12.05.2004:

What is to do be done, if a website has already stored its cookie with login information and if I want to reuse the cookie to get a page via the http page?

I read the cookie file and to pass its data like mentioned above with the "-headers [list Cookie ..." option and I changed the useragent string to look like the Internet Explorer, but nothing worked.

Any hint or suggestion?

KPV For starters try using sockspy to see what Internet Explorer is sending and how it differs from what you're sending.

male 12.05.2004:

I used sockspy and found out, what part of the already existing data inside the cookies file had to be used. Now it works fine! Thanks!


Kiko 12.01.2005:

Here's an example on how to login to some website store cookies in a global var for later use and sending defined headers...

Code...

 set website http://website.com
 set timeout 10000;#maximum of milliseconds to wait for the page
 set agent "Mozilla/4.0 (compatible\; MSIE 6.0\; Windows NT 5.0)"
 set headers(Accept) "text/html\;q=0.9,text/plain\;q=0.8,image/png,*/*"
 set headers(Accept-Language) "en-us,en\;q=0.5"
 set headers(Accept-Charset) "ISO-8859-1,utf-8\;q=0.7,*\;q=0.7"
 set headers(Referer) http://somesite.com

 #return the headers.. [headers]
 proc headers {} {
  foreach header [array names ::headers] {
   lappend head $header
   lappend head $::headers($header)
  }
  return $head
 }

 #parses the cookies out of $state(meta) to a global COOKIES var
 proc parse:cookies {meta} {
  foreach {name value} $meta {
   if {[string equal -nocase set-cookie $name]} {
    set x "[split [lindex [split $value {;}] 0] =]"
    set ::COOKIES([lindex $x 0]) [lindex $x 1]
   }
  }
  if {[info exists ::COOKIES]} {
   set f [open cookies w+]
   #puts the cookies in a file with the current unixtime for later use
   #u can instead try to improve this function and use the unixtime of the expire date from the cookie
   foreach cookie [array names ::COOKIES] {
    puts $f "$cookie $::COOKIES($cookie) [clock seconds]"
   }
   close $f
  }
 }

 #returns a "ready to use" cookie line
 proc show:cookie:line {} {
  if {[info exists ::COOKIES]} {
   foreach cookie [array names ::COOKIES] {lappend cookies $cookie=$::COOKIES($cookie)}
   return [list Cookie [join $cookies {; }]]
  }
 }

 #returns a "ready to use" headers line putting together both cookies & headers (-headers)
 proc heads {} {return "[headers] [show:cookie:line]"}

 Using the functions above then u would only need to login using:
  ::http::config -useragent $agent
  set xx [http::geturl $website -query $::string -validate 1 -type $type -headers [heads] -timeout $timeout]
  set x [split [http::data $xx] \n];upvar #0 $xx state
  parse:cookies $state(meta)
  set f [open $pagina.html w+];puts $f [join $::PAGE \n];close $f;#stores the contents of the site in an html file

 $::string is the string u'll send to the server...
 imagine u already parsed out a form from an html page and the fiels are: 2i3ut5iu2y/o3ui4g52io34g5
 2i3ut5iu2y (being a random field name for "username")
 o3ui4g52io34g5 (being a random field name for "password")
 and action=login the field to submit with the form...
 set string [::http::formatQuery action login 2i3ut5iu2y myuser o3ui4g52io34g5 mypass]

 $type is a header that some websites need on POST so just in case u can try using
 set type application/x-www-form-urlencoded

 in case u wanna reuse the cookies....
 if {[file exists cookies]} {
  set f [open cookies r];set x [split [read $f] \n];close $f
  foreach cookie $x {
   set value [lindex $cookie 1];set time [lindex $cookie 2];set cookie [lindex $cookie 0]
   #restore the cookie if it's younger than 270 seconds
   if {[string equal -nocase PHPSESSID $cookie]&&[expr [clock seconds] - $time]<"270"} {
    set ::COOKIES($cookie) $value
   }
  }
 }

This page also serves as a reference for all pages that deal with Cookie management by CGI processing scripts.


Also see Cookies: give and take


Wub utility for cookie generation and parsing

Latest version may be sourced here [L1 ] and perused here [L2 ]

CMcC is the current custodian, but help and improvement is welcome.


DKF: I'm working on an update for http to support cookies. Work in progress…