#!/opt/local/bin/perl
# tristan+perl@ethereal.net 9jan2015

use strict;
use warnings;
use MongoDB;
use Device::OUI;

# Unbuffer STDOUT
$| = 1;

# Initialize Device::OUI
Device::OUI->cache_db('device_oui'); #XXX use File::Temp
Device::OUI->cache_file('oui.txt'); #XXX use File::Temp
Device::OUI->load_cache_from_web();

# Initialize MongoDB
my $conn = MongoDB::Connection->new(host => 'localhost', port => 27117);
my $db = $conn->get_database('ace');

# Cache known APs
my @aps;
my $aps = $db->get_collection('device')->find();
while (my $doc = $aps->next) {
  push @aps, $doc;
}

# Cache connected clients - XXX need to recache periodically
my @stations;
my $stations = $db->get_collection('cache_sta')->find();
while (my $doc = $stations->next) {
  push @stations, $doc;
}

# Find last event
my $event = $db->get_collection('event');
my $cursor = $event->find()->sort({'$natural' => -1})->limit(1);
my $last_event = $cursor->next;

# Consume new records
while (1) {
  my $cursor = $event->find({_id => {'$gt' => $last_event->{_id}}});
  while (my $doc = $cursor->next) {
    process_event($doc);
    $last_event = $doc;
  }
  sleep 1;
}

sub pretty_mac {
  my $mac = shift;
  my($oui,$addr) = $mac =~ /([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}):([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})/i;
  my $result = Device::OUI->new($oui);
  my $org = $result->organization;
  return $mac if !$org;
  $org =~ s/,? (?:Incorporated|Inc\.?|Company)//i;
  $org =~ s/Hewlett-Packard/HP/i;
  $org =~ s/Nest Labs/Nest/i;
  $org =~ s/Slim Devices/Logitech/i;
  return "$mac [$org]";
}

sub ap_name {
  my $mac = shift;
  for (@aps) {
    next unless $_->{mac} eq $mac;
    # just toss the MAC if we know the name
    return $_->{name} if $_->{name};
  }
  return $mac;
}

sub process_event {
  my $e = shift;
  my $msg = $e->{msg};

  # add hostname to msg if there is one
  if ($e->{hostname}) {
    $msg =~ s/^\S+/$& ($e->{hostname})/;
  }

  # "User" is obvious
  $msg =~ s/User\[(\S+)\]/$1/;

  # resolve client MAC to IP
  my($clientmac) = $msg =~ /^([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})/i;
  if ($clientmac) {
    for (@stations) {
      next unless $_->{mac} eq $clientmac;
      next unless $_->{ip};
      my $ip = $_->{ip};
      $msg =~ s/^\S+/$& ($ip)/;
    }
  }

  # prettify client MAC
  $msg =~ s/^[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}/pretty_mac($&)/ei;

  # resolve AP MAC
  $msg =~ s/AP\[([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})\]/'AP['.ap_name($1).']'/ei;

  # strip quotes
  $msg =~ s/"//g;

  print "$msg\n";
}
