#!/usr/bin/perl

=head1 NAME dsumap

dsumap - update location on DatastormUsers.com map

=head1 SYNOPSIS

 dsumap [note]

=head1 DESCRPTION

Extracts latitude and longitude information from the D2 satellite
controller and uses this to update your location on the DatastormUsers.com
map (http://map.datastormusers.com/index.cfm).

You must have already registered at DatastormUsers.com, and manually set up
your self on the map to use this program. Then add lines to your ~/.satutilsrc
for datastormuserslogin and datastormuserspassword, and run the program
once the D2 has acquired a GPS signal (and after you're online of course).
The program should be run at least once every 10 days to keep you on the map.

If you include the note parameter it will update your note on the map.

=cut

use warnings;
use strict;
use LWP::UserAgent;
use HTTP::Cookies;
use HTML::Form;
use SatUtils::Config;
use SatUtils::D2;

my $ua = LWP::UserAgent->new;
$ua->agent("satutils");
my $cookie_jar = HTTP::Cookies->new;
$ua->cookie_jar($cookie_jar);

my $username=SatUtils::Config::get("datastormuserslogin");
my $password=SatUtils::Config::get("datastormuserspassword");
if (! defined $username) {
	die "datastormuserslogin not set in config file\n";
}
if (! defined $password) {
	die "datastormuserspassword not set in config file\n";
}

sub forumlogin {
	my $loginpage = $ua->get("http://www.datastormusers.com/cgi-bin/ultimatebb.cgi?/ubb/login.html");
	my @forms = HTML::Form->parse($loginpage);
	if (@forms != 1) {
		die "error at forum login; did not find exactly one form";
	}
	my $form=pop @forms;
	
	$form->value(username => $username);
	$form->value(password => $password);
	$ua->request($form->click);
	if (!getcookie()) {
		die "cookie not set by login";
	}
}

sub getcookie {
	my $value='';
	$cookie_jar->scan( sub {
		my ($ver, $key, $val)=@_;
		if ($key=~/^ubber[0-9]+/) {
			$value=$val;	
		}
	});
	return $value;
}

sub splitcookie {
	my $cookie=getcookie();
	my @bits=split('&', $cookie);
	my $username=$bits[2];
	my $id=$bits[4];
	return $username, $id;
}

sub maplogin {
	my ($longitude, $latitude, $note)=@_;
	
	my $continuepage = $ua->get("http://www.datastormusers.com/mylocation.cfm");
	my @forms = HTML::Form->parse($continuepage);
	if (@forms != 1) {
		die "error at map login stage 1; did not find exactly one form: @forms";
	}
	my $form=pop @forms;
	$form->value(UserName => $username);
	$form->value(ID => $password);
	my $resp=$ua->request($form->click);
	
	my ($username, $id)=splitcookie();
	$form->value(UserName => $username);
	$form->value(ID => $id);
	$resp=$ua->request($form->click);
	
	@forms = HTML::Form->parse($resp);
	@forms = grep $_->attr("name") eq "whereami", @forms;
	if (@forms != 1) {
		die "error at map login stage 2; did not find whereami form: @forms";
	}
	$form=pop @forms;
	$form->value(UserID1 => $id);
	$form->value(Latitude => coordnum($latitude));
	$form->value(LatHemi => (coordcompass($latitude) eq "N" ? 0 : 1));
	$form->value(Longitude => coordnum($longitude));
	$form->value(LongHemi => (coordcompass($longitude) eq "W" ? 0 : 1));
	if (defined $note) {
		$form->value(note => $note);
	}
	$resp=$ua->request($form->click);

	if ($resp->decoded_content !~ /Thank you./) {
		die "didn't see Thank you message after posting form, may not have worked";
	}
}

sub coordok {
	my $coord=shift;
	$coord=~m/^[0-9.]+[NSEW]$/;
}

sub coordcompass {
	my $coord=shift;
	$coord=~m/^[0-9.]+([NSEW])$/;
	return $1;
}

sub coordnum {
	my $coord=shift;
	$coord=~m/^([0-9.]+)[NSEW]$/;
	return $1;
}

my $note=shift;

my $s=SatUtils::D2->new;
my $latitude=$s->getvalue("latitude");
my $longitude=$s->getvalue("longitude");
if (! coordok($latitude) || ! coordok($longitude)) {
	die "D2 GPS not available\n";
}
my $fuzz=SatUtils::Config::get("dsumapfuzz");
if (defined $fuzz && $fuzz > 0) {
	for (my $n=0; $n < $fuzz; $n++) {
		$latitude=~s/(\d)(\d{$n}.$)$/int(rand(10)).$2/e;
		$longitude=~s/(\d)(\d{$n}.$)$/int(rand(10)).$2/e;
	}
}

forumlogin(); # sets a cookie needed for map login
maplogin($longitude, $latitude, $note);

=head1 AUTHOR

Joey Hess <joey@kitenet.net>

=cut
