#!/opt/local/bin/perl -w use strict; # this is a script to quickly logon to a kpn-hotspot. # after logging in, it waits until the user presses 'ctrl-c', to logout again. # # you should configure a username and password in '~/.hotspotrc' # our $debug=0; ##################################### # some included support nodules package WebServer; use strict; use warnings; use HTTP::Request::Common qw(POST GET); use LWP::UserAgent; use HTTP::Cookies; use List::Util qw(first); sub new { my ($class, $baseurl)= @_; my $ua= LWP::UserAgent->new(agent=>'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040501'); $ua->cookie_jar(HTTP::Cookies->new(hide_cookie2=>1)); $ua->env_proxy(); return bless { ua=>$ua, baseurl=>$baseurl, }, $class; } sub clearcookies { my $self= shift; $self->{ua}->cookie_jar(HTTP::Cookies->new(hide_cookie2=>1)); } sub getcookie { my ($self, $domain, $name)=@_; my $value; $self->{ua}->cookie_jar->scan( sub { return if $_[1]!=$name; return if $_[4]!=$domain; $value= $_[2]; }); return $value; } # almost interface compatible with httpost # - optional hashref with parameters is merged with parameters. # # httpget("/some.cgi", key1=>123, key2=>455); # httpget("/some.cgi", { urlkey1=>999 }, key1=>123, key2=>455); # sub httpget { my $self= shift; my $path= shift; my $query; if (@_) { $query= shift; if (ref $query ne "HASH") { unshift @_, $query; $query=undef; } } my %params= @_; my $uri= URI->new($self->{baseurl}); $uri->path($path); $uri->query_form($query?%$query:(), %params); my $rq= GET $uri; # todo: get rid of 'TE' header, and 'Connection'-TE flag. and 'Cookie2' header $rq->header( 'User-Agent'=> 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040501', 'Accept'=> ($path =~ /\.aspx|\.htm/ ? 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5' : $path =~ /\.css/ ? 'text/css,*/*;q=0.1' : '*/*') , 'Accept-Language'=> 'en-us,en;q=0.5', #'Accept-Encoding'=> 'gzip,deflate', 'Accept-Charset'=> 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', ); printf("request:\n%s\n", $rq->as_string) if ($main::debug); #warn "network access disabled\n"; #return; my $rp= $self->{ua}->request($rq) or die "httperror: $@\n"; printf("response:") if ($main::debug); printf("%s\n", $rp->status_line) if ($main::debug || $rp->code >= 400); printf("%s\n", $rp->headers_as_string()) if ($main::debug); printf("%s\n", $rp->content) if ($main::debug>1); return undef if $rp->code >= 400; return $rp->content if (defined(wantarray)); } # can be called in several ways: # httppost("/some.cgi", key1=>123, key2=>455); # -> just form values # httppost("/some.cgi", { urlkey1=>999 }, key1=>123, key2=>455); # -> both url and form params # httppost("/some.cgi", key1=>123, key2=>455, file1=>["filename"]); # -> form-data file upload sub httppost { my $self= shift; my $path= shift; my $query; if (@_) { $query= shift; if (ref $query ne "HASH") { unshift @_, $query; $query=undef; } } my %params= @_; my $useformdata= grep { defined ref $_ && ref $_ eq "ARRAY" } values %params; my $uri= URI->new($self->{baseurl}); $uri->path($path); $uri->query_form(%$query) if ($query); my $rq; if ( $useformdata ) { $rq = POST $uri, Content_Type=>"form-data", Content=>[ %params ]; } else { $rq = POST $uri, [ %params ]; } # -- for http uploads : # ( Content_Type=>"form-data", Content=>[ %params ]); $rq->header( 'User-Agent'=> 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040501', 'Accept'=> 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5', 'Accept-Language'=> 'en-us,en;q=0.5', #'Accept-Encoding'=> 'gzip,deflate', 'Accept-Charset'=> 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', ); printf("request:\n%s\n", $rq->as_string) if ($main::debug); #warn "network access disabled\n"; #return; my $rp= $self->{ua}->request($rq) or die "httperror: $@\n"; printf("response:") if ($main::debug); printf("%s\n", $rp->status_line) if ($main::debug || $rp->code >= 400); printf("%s\n", $rp->headers_as_string()) if ($main::debug); printf("%s\n", $rp->content) if ($main::debug>1); return undef if $rp->code >= 400; return $rp->content if (defined(wantarray)); } sub httprequest { my ($self, $method, @params)= @_; if (lc($method) eq "get") { return $self->httpget(@params); } elsif (lc($method) eq "post") { return $self->httppost(@params); } else { die "invalid http request method '$method'\n"; } } package HotSpot; # http://www.nu.nl/ # http://redirector.hotspotsvankpn.com/index.php?sdx=1 # Set-Cookie: CMSSESSID=0b9hj7gpkh37dpggte7rtclpk5; path=/ # https://portal.hotspotsvankpn.com/session.asp?CMSSESSID=0b9hj7gpkh37dpggte7rtclpk5 # Set-Cookie: hotspotsessionid=0b9hj7gpkh37dpggte7rtclpk5; expires=Thu, 31-Dec-2009 23:00:00 GMT; path=/ # Set-Cookie: ASPSESSIONIDCCBBCSSR=JDEDNIHDOIBMBKDNFDBFLOMK; path=/ # https://portal.hotspotsvankpn.com/templates/redirector.asp # Cookie: hotspotsessionid=0b9hj7gpkh37dpggte7rtclpk5; ASPSESSIONIDCCBBCSSR=JDEDNIHDOIBMBKDNFDBFLOMK # https://portal.hotspotsvankpn.com/templates/dispatcher.asp?page_id=home_hs_out_nl # Cookie: hotspotsessionid=0b9hj7gpkh37dpggte7rtclpk5; ASPSESSIONIDCCBBCSSR=JDEDNIHDOIBMBKDNFDBFLOMK # Set-Cookie: ASP.NET_SessionId=5wv0p03rweylr545ffa5q33s; path=/ # Set-Cookie: ASP.NET_SessionId=gfb3cm2bdizuvjfvhvuolz45; path=/ # POST https://portal.hotspotsvankpn.com/templates/dispatcher.asp?page_id=home_hs_out_nl # Cookie: hotspotsessionid=0b9hj7gpkh37dpggte7rtclpk5; ASPSESSIONIDCCBBCSSR=JDEDNIHDOIBMBKDNFDBFLOMK; ASP.NET_SessionId=gfb3cm2bdizuvjfvhvuolz45 # page_id=1620&ws_action=&provider=4&name=XXXXX&pass=YYYYY&sms-nummer=&sms-code=&name_purchase=&pass_purchase=&kraskaart-nummer=&kraskaart-code= sub new { my ($class, %properties)= @_; my $self= bless { redirector=> WebServer->new("http://redirector.hotspotsvankpn.com"), portal=> WebServer->new("https://portal.hotspotsvankpn.com"), %properties, }, $class; return $self; } sub load { my ($self)=@_; my $redir= $self->{redirector}->httpget("/index.php", sdx=>1) || die "error loading redirector\n"; if ($redir=~/"0;url=htt.*?CMSSESSID=(.*?)"/) { $self->{cmssessid}= $1; printf("CMSSESSID=%s\n", $self->{cmssessid}); $self->{portal}->httpget("/session.asp", CMSSESSID=>$self->{cmssessid}) || die "error loading portal\n"; } else { printf("ERROR: no cmssessid\n"); } } sub login { my ($self, $user, $pass)=@_; $self->{portal}->httppost("/templates/dispatcher.asp", {page_id=>'home_hs_out_nl'}, page_id => 1620, # ws_action => '', provider => 4, name => $user, pass => $pass, # 'sms-nummer' => '', # 'sms-code' => '', # name_purchase => '', # pass_purchase => '', # 'kraskaart-nummer' => '', # 'kraskaart-code' => '', ) || die "error logging in\n"; } sub logout { my ($self)=@_; $self->{portal}->httpget("/templates/logout2.asp", CMSSESSID=>$self->{cmssessid}) || die "error logging out\n"; } package main; use strict; use warnings; use IO::File; use Getopt::Long; $|=1; my $config= readconfig("$ENV{HOME}/.hotspotrc"); my $m= HotSpot->new(map { ($_=>$config->{$_}) } grep { $_ ne "url" && $_ ne "user" && $_ ne "pass" } keys %$config); $m->load(); $m->login($config->{user}, $config->{pass}); printf("logged in\n"); $SIG{INT}= sub { $m->logout(); printf("logged out\n"); exit(0); }; printf("\n"); while (1) { sleep(1000); } sub readconfig { my $filename=shift; my %params; my $fh= IO::File->new($filename, "r") or die "$filename: $!"; while (<$fh>) { if (/^(.+?)\s*=\s*(.*?)\s*$/) { my ($k, $v)= ($1, $2); $params{$k}= $v; } } $fh->close(); return \%params; }