#!/usr/bin/perl

use Text::Wrap;
use Term::Cap;
use Term::ReadKey;
use Getopt::Long;
use Pod::Usage;
use sigtrap qw(die normal-signals);

##########################
# Main Program Code/Loop #
##########################

# Initialize variables
InitVars();

Getopt::Long::Configure ("bundling");


my $man = 0;
my $help = 0;
my $talkmode = 0;
my $listenmode = 0;
my $interactivemode = 0;

GetOptions('help|?|h'                => \$help,
           'man'                     => \$man,
	   'talk|t'                  => \$talkmode,
	   'listen|l'                => \$listenmode,
	   'interactive|i'           => \$interactivemode,
	   'quiet|q'                 => \$quiet,
	   'timeout|n=i'             => \$timeout,
	   'file|f=s'                => \$usefilename,
	   'rate|r=i'                => \$rate,
	   'continuous|noautorate|c' => \$noautorate,
	   'priority|p=i'            => \$priority,
	   'switch|tcp|s'            => \$switchprotocols
		   ) or pod2usage(2);
pod2usage(1) if $help;
pod2usage(-exitstatus => 0, -verbose => 2) if $man;
pod2usage("$0: Please specify only one mode option") if ($talkmode + $listenmode + $interactivemode > 1);

$interactivemode = 1 if ($talkmode + $listenmode + $interactivemode == 0);

# Process arguments
pod2usage("$0: You can only specify a timeout for listen mode") if (not $listenmode and $timeout > 0);
pod2usage("$0: Timeout must be a positive value") if ($timeout < 0);
pod2usage("$0: You can only specify a file for talk and listen modes") if ($interactivemode and $usefilename ne "");
pod2usage("$0: You can only specify a priority for talk mode") if (not $talkmode and $priority != 0);
pod2usage("$0: Priority must be between -128 and 127") if ($priority < -128 or $priority > 127);
pod2usage("$0: You can only specify a rate when sending a file with talk mode") if ($rate > 0 and ($usefilename eq "" or $listenmode));
pod2usage("$0: Rate must be a positive interger") if ($rate < 0);
pod2usage("$0: Option \"c\" may only be specified in talk mode") if ($noautorate and not $talkmode);
$loadbroadcast =~ s/^./chr(1)/e if $switch;

# Process mode argument
if ($listenmode) {
	# Listen mode
	if ($wholeIP = shift @ARGV) {
		if ($wholeIP =~ /^\s*(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/?\d{0,2}\s*$/) {
			$connectIP = "$1.$2.$3.$4";
			if ($wholeIP =~ /\/(\d{1,2})\s*$/) {
				$connectMask = $1;
			} else {
				$connectMask = 32;
			}
		} else {
			pod2usage("$0: expected IP address at $wholeIP");
		}
	} else {
		$connectIP = "0.0.0.0";
		$connectMask = 0;
	}
	if ($temp = shift @ARGV) {
		pod2usage("$0: Unknown argument $temp");
	}
	LoadModules();
	SetSize();
	if ($timeout > 0) {
		$endString = " SpyTalk will timeout in $timeout seconds.";
	} else {
		$endString = " Send EOF (^D) to stop listening.";
	}
	print wrap("", "", "Now listening for messages from $connectIP/$connectMask.$endString") . "\n" if (not $quiet);
	OpenConnection($connectIP, $connectMask, 1);
	if ($usefilename ne "") {
		open(OUTPUT, ">$usefilename") or die "Could not open output file ($!)";
	} else {
		open(OUTPUT, ">&STDOUT") or die "Could not replicate STDOUT handle ($!)";
	}
	ReadMode 3;
	select OUTPUT;
	$| = 1;
	while ((($key = ReadKey(-1)) ne "\cD" and $timeout <= 0) || (time - $starttime < $timeout and $timeout > 0)) {
		@recvmesg = ReadMesg();
		print OUTPUT GetMesg("", @recvmesg);
	}
	if ($usefilename eq "") {
		print "\n";
	} else {
		close OUTPUT;
	}
	print wrap("", "", "Connection closed.") . "\n" if (not $quiet);
	exit(0);
} elsif ($talkmode) {
	# Talk mode
	if ($wholeIP = shift @ARGV) {
		if ($wholeIP =~ /^\s*(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s*$/) {
			$ipmesg = chr(1) . chr($1) . chr($2) . chr($3) . chr($4) . " " . chr(5);
		} else {
			pod2usage("$0: expected IP address at $wholeIP");
		}
	} else {
		pod2usage("$0: expected IP address");
	}
	if ($temp = shift @ARGV) {
		pod2usage("$0: Unknown argument $temp");
	}
	LoadModules();
	SetSize();
	if ($usefilename ne "") {
		print wrap("", "", "$usefilename is being sent to $wholeIP. Sending will stop when the end of the file is reached.") . "\n" if (not $quiet);
		open(INPUT, "<$usefilename") or die "Could not open input file ($!)";
	} else {
		print wrap("", "", "Type to send messages to $wholeIP. Send EOF (^D) to stop sending.") . "\n" if (not $quiet);
		open(INPUT, "<&STDIN") or die "Could not replicate STDIN handle ($!)";
	}
	ReadMode 3, INPUT;
	$currentsendtime = 0;
	if ($usefilename ne "" and not $noautorate) {
		$maxsendqueuelength = GetSendQueueLength() + 100;
	}
	while (1) {
		if ($rate > 0) {
			if (time <= $currentsendtime) {
				next if ($sendcount >= $rate);
			} else {
				$currentsendtime = time;
				$sendcount = 0;
			}
			$sendcount++;
		} elsif ($usefilename ne "" and not $noautorate) {
			next if (GetSendQueueLength() >= $maxsendqueuelength);
		}
		last if ($usefilename ne "" and eof(INPUT));
		$key = ReadKey(0, INPUT);
		last if ($usefilename eq "" and $key eq "\cD");
		print $key if ($usefilename eq "");
		open(SEND, ">/proc/ipdsend") || die "Could not open send module for writing";
		SendMesg($key);
		$num = ($num + 1) % 256;
		close SEND;
	}
	if ($usefilename eq "") {
		print "\n";
	} else {
		close INPUT;
	}
	print wrap("", "", "Connection closed.") . "\n" if (not $quiet);
	exit(0);
} elsif ($modearg = shift @ARGV) {
	# Unknown argument
	pod2usage("$0: Unknown argument $temp");
}


# If it got through the tests, it's interactive fullscreen mode

# Initialize everything
LoadModules();
SetSize();
InitBuffers();
$terminal = GetTerminal();

# For lack of a better way of guaranteeing we know what backspace sends
system "stty erase ^?";

# Unbuffered mode; read keys immediately
ReadMode 3;

# Main full-screen program loop
while ($locallines[1] !~ /^quit\n$/i) {
	# Open the send interface
	open(SEND, ">/proc/ipdsend") || die "Could not open send module for writing";

	# Read load average
	$localload = LoadAvg();

	# Display the screen, but only if a key was typed or it's been a second
	if (defined $key || time > $keytime) {
		$keytime = PrintFullScreen($terminal);
	}

	# Get the next key and process it, but if there is no key waiting, return immediately
	$key = ReadKey -1;
	AddKey($key);

	# Increment the key number if a key was pressed
	$num = ($num + 1) % 256 if defined $key;

	# If it's connected, send the key and read any incoming data
	if (&Connected) {
		SendMesg($key);
		@recvload = ReadLoad();
		$remoteload = GetLoad(@recvload);
		@recvmesg = ReadMesg();
		GetMesg(true, @recvmesg);
	}

	# Send the load out
	SendLoad($localload);

	# Look for the command that opens connections
	if ($locallines[0] =~ /^talk\s+(.*)\n$/is) {
		OpenConnection($1, 32);
	}

	# Look for the command that closes connections
	if (($locallines[0] =~ /^close\n$/is || $locallines[0] =~ /^quit\n$/is) && &Connected) {
		Disconnect();
	}

	# Look for the set priority command
	if ($locallines[0] =~ /^set\s+priority\s+(-?\d+)\n$/is) {
		$priority = ($1 > 127) ? 127 : (($1 < -128) ? -128 : $1);
	}

	# Check to see if we should wrap the current line or if enter was pressed
	@locallines = CheckNewLines(@locallines);

	# Close the send interface; because of buffering we need to open and close it every time we change something.
	close SEND;
}
ClearInfiniteSend();

# Always run at end, even if the script died or was killed
END {	ReadMode 0, INPUT if ($talkmode and $usefilename eq ""); ReadMode 0; RemoveModules() if ($installedmods > 0);	}

###################################################
# End of Main Code/Loop, only functions past here #
###################################################

sub DisplayMessage {
	return if $quiet;
	$remotelines[0] .= "\n";
	unshift @remotelines, @_;
	unshift @remotelines, ("");
	while (shift @_) {
		pop @remotelines;
	}
	pop @remotelines;
	return;
}

sub LoadModules {
	$installedmods = 0;
	$modules = `lsmod`;
	if ($modules !~ /ipdsend/i and not $listenmode) {
		$temp = `insmod ipdsend`;
		$installedmods += 1;
	}
	if ($modules !~ /ipdrecv/i and not $talkmode) {
		$temp = `insmod ipdrecv`;
		$installedmods += 2;
	}
}

sub RemoveModules {
	if ($installedmods % 2) {
		$temp = `rmmod ipdsend`;
	}
	if ($installedmods >= 2) {
		$temp = `rmmod ipdrecv`;
	}
}

sub SetSize {
	($cols, $rows) = GetTerminalSize;
	$cols = 80 if not defined $cols;
	$rows = 25 if not defined $rows;

	$Text::Wrap::columns = $cols;
	$linehistory = int($rows / 2 - 2); #/ # for kate's syntax highlighter
}

sub InitBuffers {
	@locallines = ("\n") x $linehistory;
	@remotelines = ("\n") x $linehistory;
	$remotelines[0] = "";

	@intro = split(/\n/, wrap("\t", "", (
	"Welcome to the SpyTalk IP Diffusion load dispersion and messaging demonstration!",
	"Your computer will now begin transmitting load information.",
	"To open a session with another computer, type \"talk <computer>\"",
	"where <computer> is the IP address or host name of the computer you want to talk to.",
	"You will then receive messages from that computer as well as load information.",
	"Anything you type after that will be sent to the other computer, until you type \"close\" or \"quit\".",
	"The success of messaging is of course dependant on the existence of other network traffic,",
	"so be sure there is traffic between connected computers.")));

	if (not $quiet) {
		while (@intro > 0) {
			unshift @locallines, ((shift @intro) . "\n");
			pop @locallines;
		}
	}
	unshift @locallines, ("");
	pop @locallines;
}

sub InitVars {
#	$modulepath = "/home/brian/ipd"; -- Modules don't need a path if installed correctly
	$loadbroadcast = chr(2) . chr(0) . chr(0) . chr(0) . chr(0) . chr(0) . chr(255) . chr(0);
	$filenamePrefix = "SpyTalk";
	$filenameNumber = 0;
	$command = "";
	$openIP = "Not connected";
	$ipmesg = "";
	$num = 0;
	$priority = 0;
	$remoteload = "";
	$IP = `uname -n`; #"
	$IP =~ s/\s//g;
	$localload = "";
	$remotenum = -1;
	%remotedata = ();
	%remotecount = ();
	$quiet = "";
	$timeout = 0;
	$starttime = time;
	$usefilename = "";
	$switchprotocols = 0;
	$rate = 0;
	$noautorate = 0;
	$tcpregexall = '^(\d+\s+-\s+\d+\.\d+\s+-\s+\d+\.\d+\.\d+\.\d+:\d+\s+-\s+\d+\.\d+\.\d+\.\d+:\d+\s+- ..\d{1,2}\s*)$';
	$ipregexall = '^(\d+\s+-\s+\d+\.\d+\s+-\s+\d+\.\d+\.\d+\.\d+\s+-\s+\d+\.\d+\.\d+\.\d+\s+- ..\d\s*)$';
	$tcpregexdata = '^\d+\s+-\s+\d+\.\d+\s+-\s+\d+\.\d+\.\d+\.\d+:\d+\s+-\s+\d+\.\d+\.\d+\.\d+:\d+\s+- (..)\s*$';
	$ipregexdata = '^\d+\s+-\s+\d+\.\d+\s+-\s+\d+\.\d+\.\d+\.\d+\s+-\s+\d+\.\d+\.\d+\.\d+\s+- (..\d)\s*$';
}

sub LoadAvg {
	open(LOADAVG, "</proc/loadavg");
	my $localload = join("\n", (<LOADAVG>));
	close LOADAVG;
	$localload =~ /^(\d+\.\d+)\s/;
	$localload = $1;
	return $localload;
}

sub PrintFullScreen {
	my $terminal = $_[0];
	$terminal->Tputs('cl', 1, STDOUT);
	$terminal->Tgoto('cm', 0, 0, STDOUT);
	print "Remote host: $openIP\tRemote load: $remoteload\n";
	print join("", reverse(@remotelines));
	print "\n/\\ Remote messages" . ("-" x (($cols / 2) - 22)) . "SpyTalk" . ("-" x (($cols / 2) - 21)) . "Local messages \\/\n"; #/
	$temp = "Local host: $IP\tLocal load: $localload\tPriority: $priority";
	if (wrap("", "", $temp) =~ /\n/) {
		$temp = "Local host: $IP\tLocal load: $localload";
	}
	if (wrap("", "", $temp) =~ /\n/) {
		$temp = "Local load: $localload";
	}
	print "$temp\n";
	print join("", reverse(@locallines));
	print "\n";
	return scalar time;
}

sub ReadLoad {
	if ($switch) {
		$protoAcro = "IP";
		$regex = $ipregexall;
	} else {
		$protoAcro = "TCP";
		$regex = $tcpregexall;
	}
	open (RECVLOAD, "</proc/ipd/" . $filenamePrefix . $protoAcro . $filenameNumber) || die "Could not open $protoAcro read buffer";
	my @recvload = <RECVLOAD>;
	close RECVLOAD;
	open (RECVLOAD, ">/proc/ipd/" . $filenamePrefix . $protoAcro . $filenameNumber) || die "Could not clear $proroAcro read buffer";
	print RECVLOAD "reset";
	close RECVLOAD;
	return join("", @recvload) =~ /$regex/smig;
}
sub ReadMesg {
	if ($switch) {
		$protoAcro = "TCP";
		$regex = $tcpregexall;
	} else {
		$protoAcro = "IP";
		$regex = $ipregexall;
	}
	open (RECVMESG, "</proc/ipd/" . $filenamePrefix . $protoAcro . $filenameNumber) || die "Could not open $protoAcro read buffer";
	my @recvmesg = <RECVMESG>;
	close RECVMESG;
	open (RECVMESG, ">/proc/ipd/" . $filenamePrefix . $protoAcro . $filenameNumber) || die "Could not clear $proroAcro read buffer";
	print RECVMESG "reset";
	close RECVMESG;
	return join("", @recvmesg) =~ /$regex/smig;
}

sub GetLoad {
	if ($switch) {
		$regex = $ipregexdata;
	} else {
		$regex = $tcpregexdata;
	}
	while ($recvload = shift @_) {
		#extract load info if valid data
		next if $recvload =~ /^Messages/i;
		if ($recvload =~ /$regex/smi) {
			next if substr($1, 2, 1) eq "0";
			$remoteload = 256 * ord(substr($1, 0, 1));
			$remoteload += ord(substr($1, 1, 1));
			$remoteload /= 100; #/ # for kate's syntax highlighting
		} else {
			next;
		}
	}
	return $remoteload;
}

sub GetMesg {
	my $interpretPosition = shift @_;
	my $returnString = "";
	if ($switch) {
		$regex = $tcpregexdata;
	} else {
		$regex = $ipregexdata;
	}
	while ($recvmesg = shift @_) {
		#extract $recvdata and $recvnum from $recvmesg or skip if not valid data
		next if $recvmesg =~ /^Messages/i;
		if ($recvmesg =~ /$regex/smi) {
			$recvnum = ord(substr($1, 0, 1));
			$recvdata = ord(substr($1, 1, 1));
			next if substr($1, 2, 1) eq "0";
		} else {
			next;
		}

		if ($remotenum == -1) {
			$remotenum = $recvnum - 1;
		}
		if ($recvdata != $remotedata{$recvnum} || $remotecount{$recvnum} >= 5 || $remotecount{$recvnum} <= 0) {
			$remotedata{$recvnum} = $recvdata;
			$remotecount{$recvnum} = 1;
			if ($interpretPosition) {
				if ($recvnum == ($remotenum + 1) % 256) {
					#received is next character
					$remotelines[0] .= chr($recvdata);
				} elsif ($recvnum == $remotenum) {
					#received is in the same place as the last character
					$remotelines[0] =~ s/.$/chr($recvdata)/e;
				} elsif (($recvnum - $remotenum) % 256 < 128) {
					#received is after current position
					$remotelines[0] .= (' ' x ((($recvnum - $remotenum) % 256) - 1)) . chr($recvdata);
				} else {
					#received is before current position
					$curline = 0;
					$curoffset = (($remotenum - $recvnum) % 256) + 1;
					while ($curline < $linehistory) {
						if ($curoffset > length $remotelines[$curline]) {
							$curoffset -= length $remotelines[$curline++];
							next;
						}
						substr($remotelines[$curline], -$curoffset, 1) = chr($recvdata);
						last;
					}
				}
			} else {
				if ($recvnum == ($remotenum + 1) % 256) {
					$returnString .= chr($recvdata);
				} elsif (($recvnum - $remotenum) %256 < 30) {
					$returnString .= (' ' x ((($recvnum - $remotenum) % 256) - 1)) . chr ($recvdata);
				}
			}
		} else {
			$remotecount{$recvnum}++;
		}
		if ($recvnum > $remotenum || ($recvnum < 5 && $remotenum > 250)) {
			$remotenum = $recvnum;
		}
		@remotelines = CheckNewLines(@remotelines);
	}
	return $returnString;
}

sub OpenConnection {
	my $mask = $_[1];
	my $IPonly = $_[2];
	$IPonly = "" if not defined $IPonly;
	$mask = 32 if not defined $mask;
	if (&Connected) {
		DisplayMessage("You must disconnect first!\n");
	} else {
		my $remotehost = $_[0];
		if ($remotehost !~ /\d+\.\d+\.\d+\.\d+/) {
			$remotehost = `host $remotehost`; #"
		}
		if ($remotehost =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
			$openIP = "$1.$2.$3.$4";
			$ipmesg = chr(1) . chr($1) . chr($2) . chr($3) . chr($4) . " " . chr(5);
			$ipmesg =~ s/^./chr(2)/e if $switch;
			$filenameNumber = 0;
			while (my @files = glob("/proc/ipd/$filenamePrefix*$filenameNumber")) {
				$filenameNumber++;
			}
			if (not ($IPonly && $switch)) {
				open(RECVCTRL, ">/proc/ipd_ctrl") || die "Could not open receive control for writing";
				print RECVCTRL "add $openIP/$mask IP_DFRAG " . $filenamePrefix . "IP$filenameNumber Y H";
				close RECVCTRL;
			}
			if ((not $IPonly) || $switch) {
				open(RECVCTRL, ">/proc/ipd_ctrl") || die "Could not open receive control for writing";
				print RECVCTRL "add $openIP/$mask TCP_UNIQUE " . $filenamePrefix . "TCP$filenameNumber Y H";
				close RECVCTRL;
			}
			$remotenum = -1;
			%remotedata = ();
			%remotecount = ();
			DisplayMessage("Connected to $openIP\n");
		} else {
			DisplayMessage("Host unknown!\n");
		}
	}
}

sub SendLoad {
	my $localloadnum = int($_[0] * 100);
	my $localloadnum1 = int($localloadnum / 256); #/ #For kate's syntax highlighting
	my $localloadnum2 = $localloadnum % 256;
	print SEND $loadbroadcast . chr($localloadnum1) . chr($localloadnum2) . chr(26);
}

sub Connected {
	return $openIP ne "Not connected";
}

sub SendMesg {
	my ($key) = @_;
	if (defined $key) {
		print SEND $ipmesg . chr($priority) . chr($num) . $key . chr(26);
	}
}

sub AddKey {
	my ($key) = @_;
	if ($key eq "\c?") {
		$locallines[0] =~ s/.$//;
		$num = ($num - 2) % 256;
	} else {
		$locallines[0] .= $key if defined $key;
	}
}

sub Disconnect {
	my $IPonly = $_[0];
	$IPonly = "" if not defined $IPonly;
	$openIP = "Not connected";
	if (not $quiet) {
		$remotelines[0] .= "\n";
		unshift @remotelines, ("Disconnected");
		pop @remotelines;
	}
	if (not ($IPonly && $switch)) {
		open(RECVCTRL, ">/proc/ipd_ctrl") || die "Could not open receive control for writing";
		print RECVCTRL "del " . $filenamePrefix . "IP$filenameNumber";
		close RECVCTRL;
	}
	if ((not $IPonly) || $switch) {
		open(RECVCTRL, ">/proc/ipd_ctrl") || die "Could not open receive control for writing";
		print RECVCTRL "del " . $filenamePrefix . "TCP$filenameNumber";
		close RECVCTRL;
	}
}

sub CheckNewLines {
	my @lines = @_;
	if (wrap("", "", $lines[0]) =~ /\n/) {
		$lines[0] = wrap("", "", $lines[0]);
		$lines[0] =~ s/\n(.*)/\n/s;
		unshift @lines, ($1);
		pop @lines;
	}
	return @lines;
}

sub ClearInfiniteSend {
	open(SEND, ">/proc/ipdsend") || die "Could not open send module for writing";
	print SEND $loadbroadcast . chr(0) . chr(0) . chr(0);
	substr($loadbroadcast, 6, 1) = chr(0);
	print SEND $loadbroadcast . chr(0) . chr(0) . chr(0);
	close SEND;
}

sub DisplayUsage {
	pod2usage();
	print "Usage:\n\n",
		"SpyTalk -l [-q] [-n <timeout>] [-f <file>] [<ipaddr>[/<netmask>]]\n",
		"\tListen mode. Listens for messages from other computers.\n\n",
		"\t<ipaddr>\t- Restricts listening to messages from given IP address\n\t\t\t  or network.\n",
		"\t<netmask>\t- Treats <ipaddr> as a network with given mask rather\n\t\t\t  than single address.\n",
		"\t-n <timeout>\t- Listens for the specified number of seconds before\n\t\t\t  exiting.\n",
		"\t-f <file>\t- Writes all received messages to specified file\n\n",
		"SpyTalk -t [-q] [-f <file>] <ipaddr> [<priority>]\n",
		"\tTalk mode. Sends messages to another computer.\n\n",
		"\t<ipaddr>\t- The IP address of the computer to send messages to.\n",
		"\t<priority>\t- The priority number to assign to messages.\n",
		"\t-f <file>\t- Reads the specified file and sends it through the\n\t\t\t  channel.\n\n",
		"SpyTalk [-i] [-q]\n",
		"\tInteractive fullscreen mode. Presents you with a graphical\n\tinterface for sending and receiving messages.\n\n",
		"General options\n",
		"\t-q\t\t- Quiet mode. Don't display any messages.\n\n";
}

sub GetTerminal {
	my $ospeed = eval {
		require POSIX;
		my $termios = POSIX::Termios->new();
		$termios->getattr;
		$termios->getospeed;
	} || 9600;

	my $terminal = Term::Cap->Tgetent({ TERM => undef, OSPEED => $ospeed });

	return $terminal;
}

sub GetSendQueueLength {
	open(GSQL, "</proc/ipdsend") || die "Could not read status from send module ($!)";
	my $sendstatus = <GSQL>;
	close GSQL;
	$sendstatus =~ /^IP\/TCP\s+Entries:\s+(\d+)\s+ETH\s+Entries:\s+\d+\s*$/i;
	return $1;
}

#End, followed by pod documentation

__END__

=head1 NAME

SpyTalk - send and receive messages using IP Diffusion

=head1 SYNOPSIS

B<SpyTalk> B<-t>|B<--talk> [B<-q>|B<--quiet>] [B<-s>|B<--switch>|B<--tcp>] [B<-f>|B<--file> I<file> [B<-c>|B<--continuous>|B<--noautorate>] [B<-r>|B<--rate> I<rate>]] [B<-p>|B<--priority> I<priority>] I<ipaddr>

B<SpyTalk> B<-l>|B<--listen> [B<-q>|B<--quiet>] [B<-s>|B<--switch>|B<--tcp>] [B<-f>|B<--file> I<file>] [B<-n>|B<--timeout> I<timeout>] [I<ipaddr>[/I<mask>]]

B<SpyTalk> [B<-i>|B<--interactive>] [B<-s>|B<--switch>|B<--tcp>] [B<-q>|B<--quiet>]

=head1 DESCRIPTION

B<SpyTalk> is a messaging front end to the IP Diffusion kernel modules.

B<SpyTalk> supports three modes of operation: talk, listen, and interactive.
If no mode is specified, B<SpyTalk> defaults to the fullscreen interactive mode.

In talk mode, B<SpyTalk> normally sends all input from STDIN to the computer
specified by I<ipaddr>, until you type ^D.

In listen mode, B<SpyTalk> normally listens for all input from any source and
writes everything it receives to STDOUT until it receives a ^D on STDIN. However,
if I<ipaddr> is specified, B<SpyTalk> will listen only to that IP address, and if
I<mask> is specified, I<ipaddr> will be treated as a network with the given mask.

Interactive mode is the most capable mode of B<SpyTalk>. For a description of
this mode and a list of commands, see below.

=head1 OPTIONS

=over 4

=item B<-i>, B<--interactive>

Put B<SpyTalk> into interactive mode. This is the default, so this option is
not necessary.

=item B<-l>, B<--listen>

Put B<SpyTalk> into listen mode.

=item B<-t>, B<--talk>

Put B<SpyTalk> into talk mode.

=back

=head2 Talk mode options:

=over 4

=item B<-c>, B<--continuous>, B<--noautorate>

Do not automatically adapt the send rate to avoid large send buffers when
sending files. Queue everything as quickly as possible.

=item B<-f>, B<--file> I<file>

Send the specified I<file> over the channel and then quit, ignoring
all keyboard input and ^D's.

=item B<-p>, B<--priority> I<priority>

Uses the specified I<priority> in sending the data rather than the
default of 0. Range is -128 to 127.

=item B<-q>, B<--quiet>

Quiet mode. Surpesses all messages from B<SpyTalk>.

=item B<-r>, B<--rate> I<rate>

Queue I<rate> bytes per second when sending files. Use this option when you
know how much traffic will already be present on the network. Note that each
byte of the file is sent five times, so it is recommended that you set the
rate to one fifth the rate at which you expect to be sending packets.

=item B<-s>, B<--switch>, B<--tcp>

Uses TCP rather than IP to send messages.

=back

=head2 Listen mode options:

=over 4

=item B<-f>, B<--file> I<file>

Writes all data received to the specified I<file> rather than to STDOUT.

=item B<-n>, B<--timeout> I<timeout>

Ignores all keyboard input and instead waits I<timeout> seconds and then quits.

=item B<-q>, B<--quiet>

Quiet mode. Surpesses all messages from B<SpyTalk>.

=item B<-s>, B<--switch>, B<--tcp>

Listens for messages on TCP rather than IP.

=back

=head2 Interactive mode options

=over 4

=item B<-q>, B<--quiet>

Quiet mode. Surpesses introduction and all messages from B<SpyTalk>.

=item B<-s>, B<--switch>, B<--tcp>

Switches the protocols, using TCP for messages and IP for the load.

=back

=head1 INTERACTIVE MODE

In interactive mode, you are presented with a screen with two panels.
The top panel contains messages received from other computers, and
messages from B<SpyTalk>. The bottom panel contains everything you type.
In addition to transmitting messages, B<SpyTalk> also sends the computer's
current load in interactive mode. The current load for the local and remote
computers are displayed above their respective message panels.

Interactive mode is controlled by various commands. The commands are
case insensitive. The following commands are currently understood by
B<SpyTalk>:

=over 4

=item B<talk> I<host>

Open a connection to the computer with the specified IP address or
hostname. This opens a connection for sending and receiving messages.

=item B<set priority> I<number>

Set the sending priority for future messages to the specified number.
Range is -128 to 127.

=item B<close>

Close the currently open connection

=item B<quit>

Close any open connection and quit B<SpyTalk>.

=back

=head1 NOTES

Because of the nature of IP diffusion, B<SpyTalk> does not actually create
any network traffic. If you expect to be sending and/or receiving messages
but they are not going through, check to make sure there is other network
traffic between the source and destination.

