#!/usr/bin/perl -w
use strict;

# Based on ns04mappoint.txt 
# Copyright Marius Milner 2004.
# with Modification by TPEER, LaidBack, and RogerRabbit
#
# Perl version Copyright Sal Scotto 2004
# Enjoy
#

#
# NOTE: Make sure the .pm files are in the same directory as the script
# or you will get a "Can't locate 'modulename.pm' in @INC path blahh"
#

#
# to turn an optional component on set the value to 1, otherwise set to 0
# this script also has several command line options to perform imports as well
# to use type stumblePlugin.pl ?
#

use constant USE_ZOOM         => 1; # use zoom controls
use constant USE_SPEECH       => 1; # use speech 
use constant USE_SIGNAL_AUDIO => 0; # use midi signal str sounds
use constant USE_DEF_AUDIO    => 0; # use traditional script audio 
use constant USE_WEP_MARKER   => 1; # add wep state in voice output
use constant USE_MAPPOINT     => 1; # use map point
use constant USE_CUSTOM_PINS  => 0; # use custom pishpins
use constant AUTOSAVE_MAP     => 0; # set this to true if you wish to autosave your map
use constant AUTOSAVE_FILE    => 'C:\gps\war.ptm'; # name to autosave to, and if exists will be loaded on start
use constant TTS_VOICE_NUMBER => 0; # voice number to use
use constant TRACK_VECHICLE   => 1; # track location progress in mappoint
use constant TRACK_HISTORY    => 1; # add black dots and locations history as we go (i.e. crumbs)
use constant START_LOCATION   => 'Atlanta, GA'; # starting location
use constant ZOOM_TO_START    => 1; # Zoom to starting point

use constant WLAN_PINSET     => "Wireless LAN Data";
use constant LOC_HIST_PINSET => "Location History";
use constant LAST_PINSET     => "Last Location";
# custom pushpin icons  set this path to where the icons are stored the program will look for O1 to O4.ico and W1 to W4.ico
# if you do not want custom pushpins set the flag to 0
use constant CUSTOM_ICON_LOCATIONS => 'c:\development\perl'; 
use constant ZOOM_FILE       => 'c:\gps\zoomcontrol.txt';
use constant LOG_FILE	     => 'c:\gps\stumble.log';
use lib 'c:\development\perl'; # if the library files are not in the same dir as the script add the path here

#
# Load the libraries
#
use mplib;
use stumblelib;
use waplib;
use speechlib;

# initialize WAP Libraries i.e. load vendor list
waplib::initializeWap;

#
# Command line parsing
# Called when run from command line
#

use Getopt::Long;
my $didSomeWork = 0;
# in/out for conversion dov for show voices, debug for verbose output
my ($input,$output);
GetOptions("i=s" =>\$input,
           "o=s" => \$output,
           "v" => \&doShowVoices,
           "l" => \&doCheckLibs,
           "install" => \&doInstall,
           "help|?|h" => \&doHelp);
my $logFile;
#
# private script variables
#

my $hasGPS = 0;
my %iconIds;
my ($wlanpp,$lochistpp,$lastpp,$lastHighlighted);
my ($lastLocation,$lastLon,$lastLat,$lastAlt);
my %foundMAC;
my %newMAC;
my %spokenMAC;
my $vehicle;
my $zoom = 2;
my $scanning = 0;
my $initted = 0;

initializeScript();

#
# initialize the script
# if the filename is eval, we were called via require or outside the perl cmdline
# we use this fact to initialize under NetStumbler
#

sub initializeScript
{
	open(LOGH,"+<".LOG_FILE);
	$logFile = \*LOGH;
	my ($package, $filename, $line) = caller;
	if($filename =~ /eval/)
	{
		# Setup map data
		if(USE_MAPPOINT)
		{
			mplib::initializeMap;
			# setup map
			if(AUTOSAVE_MAP)
			{
				if(-e AUTOSAVE_FILE)
				{
					mplib::loadMap(AUTOSAVE_FILE);
				}
				else
				{
					mplib::newMap
					mplib::saveMap(AUTOSAVE_FILE);				
				}
			}
			else
			{
				mplib::newMap;
			}
			mplib::showMap;
			# setup icons
			if(USE_CUSTOM_PINS)
			{
				for(my $i=1;$i<=4;$i++)
				{
					my $sName = strengthToName($i);
					$iconIds{"O$i"} = mplib::addSymbol("Open AP: $sName",CUSTOM_ICON_LOCATIONS . '\\' . "O$i.ico");
					$iconIds{"W$i"} = mplib::addSymbol("Closed AP: $sName",CUSTOM_ICON_LOCATIONS . '\\' . "W$i.ico");
				}
			}
			else
			{
				for(my $i=1;$i<=4;$i++)
				{
					my $sName = strengthToName($i);
					$iconIds{"O$i"} = $i + 33;
					$iconIds{"W$i"} = $i + 49;
				}	
			}
			# setup pushpin sets
			$wlanpp = mplib::addPushpinSet(WLAN_PINSET);
			if(TRACK_HISTORY)
			{
				$lochistpp = mplib::addPushpinSet(LOC_HIST_PINSET);
			}
			if(TRACK_VECHICLE)
			{
				$lastpp = mplib::addPushpinSet(LAST_PINSET);
				my $startLoc = mplib::findCity(START_LOCATION);
				$vehicle = mplib::findPushpin(LAST_PINSET) or $vehicle = mplib::addPushpin($startLoc,LAST_PINSET);				
				my $zloc = mplib::getPushpinProperty($vehicle,"Location");
				mplib::setPushpinProperty($vehicle,"Symbol","82");
				mplib::setPushpinProperty($vehicle,"Highlight","1");
				mplib::addPushpinToSet($vehicle,$lastpp);
				if(ZOOM_TO_START)
				{
					mplib::gotoLocation($zloc);
				}
			}
			AddItemContextMenu("doHighlight", "Highlight on map");
		} # End use Mappoint
		if(USE_SPEECH)
		{
			speechlib::initializeSpeech;
			speechlib::setVoice(TTS_VOICE_NUMBER);
			my $vd = speechlib::getVoiceDesc(TTS_VOICE_NUMBER);
			speechlib::speak("Voice feedback activated, using voice $vd");
		}
		$initted = 1;
	}
	else
	{
		doCMDLine();
	}
}

#
# Net Stumbler Hooks
#

sub OnEnableScan
{
	$scanning = 1;
}
sub OnDisableScan
{
	$scanning = 0;
	if(USE_MAPPOINT)
	{
		mplib::gotoPushpinSet($wlanpp);
		if(AUTOSAVE_MAP)
		{
			mplib::saveMap(AUTOSAVE_FILE);
			mplib::setSaveFlag;
		}
	}
	close($logFile);
}
sub OnScanComplete
{
	my ($foundNew,$seenBefore,$lostContact,$bestSNR) = @_;
	my $fMacs = keys(%newMAC);
	if(USE_SPEECH && $fMacs > 0)
	{
		my @vals = values(%newMAC);
		foreach(@vals)
		{
			speechlib::speak($_);
		}
		%newMAC = ();
	}
	if(USE_DEF_AUDIO)
	{
		if($foundNew > 0) { PlaySound("ns-aos-new.wav"); }
		elsif($lostContact > 0) { PlaySound("ns-low.wav"); }
		elsif($seenBefore > 0)
		{
			if(USE_SIGNAL_AUDIO)
			{
				if($bestSNR >= 60) { PlaySound("ns-signal-6.wav"); }
				elsif($bestSNR >= 50) { Playsound("ns-signal-5.wav"); }
				elsif($bestSNR >= 40) { Playsound("ns-signal-4.wav"); }
				elsif($bestSNR >= 30) { Playsound("ns-signal-3.wav"); }
				elsif($bestSNR >= 20) { Playsound("ns-signal-2.wav"); }
				elsif($bestSNR >= 10) { Playsound("ns-signal-1.wav"); }
				else { PlaySound("ns-signal-0.wav"); }
			}
		}
		else { PlaySound("ns-tick.wav"); }
	}
}
sub OnScanResult
{
	my($sid,$mac,$flags,$signal,$noise,$ls) = @_;
	my $osid = $sid;
	if(USE_SPEECH && !$spokenMAC{$mac})
	{
		if($sid eq "" or length($sid) <= 1)
		{
			$sid = "Hidden SID";
		}
		$osid = $sid;
		if(USE_WEP_MARKER)
		{
			if(stumblelib::isWEP($flags))
			{
				$sid .= " is Closed";
			}
			else
			{
				$sid .= " is Open";
			}
		}
		if(!$foundMAC{$mac} && USE_MAPPOINT && $hasGPS && $scanning)
		{
			my $pp = mplib::findPushpin($mac);
			unless($pp) { $pp = mplib::addPushpin($lastLocation,$mac);}
			mplib::addPushpinToSet($pp,$wlanpp);
			# Cust icon code here
			my $snrL;
			my $snr = $signal - $noise;
			if($snr > 20) { $snrL = "4";}
			elsif($snr > 18) { $snrL = "3"; }
			elsif($snr > 12) { $snrL = "2"; }
			else {$snrL = "1"; }
			if(stumblelib::isWEP($flags))
			{
				mplib::setPushpinProperty($pp,"Symbol",$iconIds{"W$snrL"});
			}
			else
			{
				mplib::setPushpinProperty($pp,"Symbol",$iconIds{"O$snrL"});				
			}
			#
			$foundMAC{$mac} = $pp;
			my $note = createNote($osid,$mac,$flags,$snr);
			mplib::setPushpinProperty($pp,"Note",$note);
			if(AUTOSAVE_MAP)
			{
				mplib::saveMap(AUTOSAVE_FILE);
				mplib::setSaveFlag;
			}
		}
		$newMAC{$mac} = $sid;
		$spokenMAC{$mac} = 1;
	}
}
sub OnScanStart
{
	# starting scan cycle
}
sub OnGPSTimeout
{
	$hasGPS = 0;
}
sub OnGPSNoFix
{
	$hasGPS = 0;
}
sub OnGPSPosition
{
	my($lat,$lon,$alt) = @_;
	if(USE_MAPPOINT && $scanning)
	{
		if(sprintf("%.4f",$lastAlt) == sprintf("%.4f",$alt) && sprintf("%.4f",$lastLon) == sprintf("%.4f",$lon) && sprintf("%.4f",$lastLat) == sprintf("%.4f",$lat))
		{
			return;
		}
		$lastAlt = $alt;
		$lastLon = $lon;
		$lastLat = $lat;
		$lastLocation = mplib::getLocation($lat,$lon,$alt);
		# Drop breadcrumbs
		if(TRACK_HISTORY)
		{
			my $pp = mplib::findPushpin("$lat $lon");
			unless($pp) { $pp = mplib::addPushpin($lastLocation,"$lat $lon");}
			mplib::setPushpinProperty($pp,"Symbol","16");
			mplib::addPushpinToSet($pp,$lochistpp);
		}
		if(TRACK_VECHICLE)
		{
			mplib::setPushpinProperty($vehicle,"Location",$lastLocation);
			if($scanning || !$hasGPS)
			{
				if(USE_ZOOM)
				{
					my $nloc = mplib::getLocation($lat,$lon,doZoom());
					mplib::gotoLocation($nloc);
				}
				else
				{
					mplib::gotoPushpinSet($lastpp);
				}
			}
		}
	}
	$hasGPS = 1;
    	
}
sub OnGPSSpeed
{
	my $speed = shift;
}
sub OnIPChange
{
	my($addr,$mask) = @_;
}
sub OnPositionChange : locked method
{
	my($sid,$mac,$flags,$snr,$lat,$lon,$alt,$fixed) = @_;
	if(!$scanning)
	{
		OnEnableScan();
	}
	if(USE_MAPPOINT)
	{
		print $logFile "$sid\t$mac\t$flags\t$snr\t$lat\t$lon\n";
		my $nloc = mplib::getLocation($lat,$lon,$alt);
		my $pp = $foundMAC{$mac};
		if($pp)
		{
			mplib::setPushpinProperty($foundMAC{$mac},"Location",$nloc);	
		}
		else
		{
			my $pp = mplib::findPushpin($mac);
			unless($pp) { $pp = mplib::addPushpin($nloc,$mac);}
			mplib::addPushpinToSet($pp,$wlanpp);
			# Cust icon code here
			my $snrL;
			if($snr > 20) { $snrL = "4";}
			elsif($snr > 18) { $snrL = "3"; }
			elsif($snr > 12) { $snrL = "2"; }
			else {$snrL = "1"; }
			if(stumblelib::isWEP($flags))
			{
				mplib::setPushpinProperty($pp,"Symbol",$iconIds{"W$snrL"});
			}
			else
			{
				mplib::setPushpinProperty($pp,"Symbol",$iconIds{"O$snrL"});				
			}
			#
			$foundMAC{$mac} = $pp;
			my $note = createNote($sid,$mac,$flags,$snr);
			mplib::setPushpinProperty($pp,"Note",$note);
		}
		if(AUTOSAVE_MAP)
		{
			mplib::saveMap(AUTOSAVE_FILE);
			mplib::setSaveFlag;
		}
	}
}

#
# Private subroutines
#

#
# Do conversion Work
#

sub doCMDLine
{
	mplib::initializeMap;
	# setup map
	unless(defined $output && $input)
	{
		unless($didSomeWork) { doHelp(); }
		else { exit 0; }
	}
	if(-e $output)
	{
		mplib::loadMap($output);
	}
	else
	{
		mplib::newMap
		mplib::saveMap($output);				
	}
	#mplib::showMap;
	# setup icons
	if(USE_CUSTOM_PINS)
	{
		for(my $i=1;$i<=4;$i++)
		{
			my $sName = strengthToName($i);
			$iconIds{"O$i"} = mplib::addSymbol("Open AP: $sName",CUSTOM_ICON_LOCATIONS . '\\' . "O$i.ico");
			$iconIds{"W$i"} = mplib::addSymbol("Closed AP: $sName",CUSTOM_ICON_LOCATIONS . '\\' . "W$i.ico");
		}
	}
	else
	{
		for(my $i=1;$i<=4;$i++)
		{
			my $sName = strengthToName($i);
			$iconIds{"O$i"} = $i + 33;
			$iconIds{"W$i"} = $i + 49;
		}	
	}
	$wlanpp = mplib::addPushpinSet(WLAN_PINSET);
	# load the summary
	if(stumblelib::isSummary($input))
        {
            my $counter = 0;
            open(FH,$input);
            while(<FH>)
            {
            	unless(/^#/)
		{
			my @line = stumblelib::parseNSSummaryLine($_);
			print "Processing AP $counter\n";
			next unless @line;
			my $nloc = mplib::getLocation($line[0],$line[1]);
			my $pp = mplib::findPushpin($line[4]);
			unless($pp) { $pp = mplib::addPushpin($nloc,$line[4]); }
			# Cust icon code here
			my $snrL;
			if($line[6] > 20) { $snrL = "4";}
			elsif($line[6] > 18) { $snrL = "3"; }
			elsif($line[6] > 12) { $snrL = "2"; }
			else {$snrL = "1"; }
			if(stumblelib::isWEP($line[9]))
			{
				mplib::setPushpinProperty($pp,"Symbol",$iconIds{"W$snrL"});
			}
			else
			{
				mplib::setPushpinProperty($pp,"Symbol",$iconIds{"O$snrL"});				
			}
			my $note = createNote($line[2],$line[4],$line[9],$line[6]);
			mplib::setPushpinProperty($pp,"Note",$note);
			mplib::addPushpinToSet($pp,$wlanpp);
			$counter++;
		}
            }
            close(FH);
            mplib::saveMap($output);
        }
        elsif(stumblelib::isNS1($input))
        {
            my @lst = stumblelib::parseNS1($input);
            my $aref;
            my $counter = 0;
            for $aref ( @lst )
            {
                print "Processing AP $counter\n";
		my $nloc = mplib::getLocation($aref->[0],$aref->[1]);
		my $pp = mplib::findPushpin($aref->[4]);
                unless($pp) { $pp = mplib::addPushpin($nloc,$aref->[4]); }
                # Cust icon code here
                my $snrL;
                if($aref->[6] > 20) { $snrL = "4";}
                elsif($aref->[6] > 18) { $snrL = "3"; }
                elsif($aref->[6] > 12) { $snrL = "2"; }
                else {$snrL = "1"; }
                if(stumblelib::isWEP($aref->[9]))
                {
                        mplib::setPushpinProperty($pp,"Symbol",$iconIds{"W$snrL"});
                }
                else
                {
                        mplib::setPushpinProperty($pp,"Symbol",$iconIds{"O$snrL"});				
                }
                my $note = createNote($aref->[2],$aref->[4],$aref->[9],$aref->[6]);
                mplib::setPushpinProperty($pp,"Note",$note);
                mplib::addPushpinToSet($pp,$wlanpp);
                $counter++;
            }
        }
}

sub createNote
{
	my ($sid,$mac,$flag,$snr) = @_;
	my $vendor = waplib::getVendorForBBSID($mac);
	my $note = "SSID\t: $sid\r\n";
	$note .="BSSID\t: $mac\r\n";
	unless($vendor) { $vendor = "Unknown"; }
	$note .="Vendor\t: $vendor\r\n";
	if(stumblelib::isWEP($flag)) {$note .="Secure\t: WEP\r\n";}
	else { $note .="Secure\t: OPEN\r\n";}
	if(stumblelib::isAdhoc($flag)) { $note .="Mode: AdHoc\r\n"; }
	else { $note .="Mode\t: Infrastructure\r\n"; }
	$note .="SNR\t: $snr\r\n";
	return $note;
}

sub doShowVoices
{
	$didSomeWork = 1;
	my %list;
	my ($key,$val);
	%list = speechlib::getVoices;
	foreach $key (sort(keys(%list)))
	{
		$val = $list{$key};
		print "Voice $key: $val\n";
	}
}

sub doCheckLibs
{
	$didSomeWork = 1;
	eval 
	{ 
		print "MS MapPoint installed: ";
		if(mplib::hasLibrary) { print "True\n"; }
		else { print "False\n"};
		print "MS VoiceTTS installed: ";
		if(speechlib::hasLibrary) { print "True\n"; }
		else { print "False\n"; }
	};
}

sub doHelp
{
    print "Usge $0 <options...>\n";
    print "\t-i <input file> This should eb a netstumbler 0.4 summary export file\n";
    print "\t-o <output file> This should be the name of the map you wish to save\n";
    print "\t   Please note: if this map already exists it will be updated and saved\n";
    print "\t-v Show all installed voices\n";
    print "\t-l Show your installed libraries\n";
    print "\t-install Install this script into netstumbler assuming current directory\n";
    print "\t--help|-h|-?  This screen\n\n";
    exit 0;
}

sub strengthToName
{
    my $str = shift;
    if($str == 1)
    {
        return "Poor";
    }
    if($str == 2)
    {
        return "Moderate";
    }
    if($str == 3)
    {
        return "Good";
    }
    if($str == 4)
    {
        return "Excellent";
    }
}

sub doZoom
{
	open(FH,ZOOM_FILE) or return "2"; 
	my $line = <FH>;
	chomp($line);
	if($line =~ /\d+/)
	{
		return $line;
	}
	else
	{
		return "2";
	}
}

sub doHighlight
{
	my $mac = shift;
	my $sid = shift;
   	$lastHighlighted = mplib::findPushpin($mac);
   	my $bool = mplib::getPushpinProperty($lastHighlighted,"Highlight");
   	if($bool != 0)
   	{
   		mplib::setPushpinProperty($lastHighlighted,"Highlight",-1);
   		mplib::setPushpinProperty($lastHighlighted,"BallonState",0);   		   		
   	}
   	else
   	{
   		mplib::setPushpinProperty($lastHighlighted,"Highlight",1);
   		mplib::setPushpinProperty($lastHighlighted,"BallonState",2);   		   		   	
   	}
}

sub doInstall
{
	my $key= new Win32::TieRegistry "CUser/Software/Bogosoft/NetStumbler/Settings",
      	{ Access=>KEY_READ()|KEY_WRITE(), Delimiter=>"/" };
	my $scriptFile  = Win32::GetCwd() + "\\"+$0;
	$key->SetValue("Script Name",$scriptFile);
	$key->SetValue("Script Type","0x00000002","REG_DWORD");
}