Merge branch 'master' of defaria.com:/opt/git/clearscm
authorAndrew DeFaria <Andrew.DeFaria@Broadcom.com>
Mon, 21 Nov 2016 23:47:08 +0000 (15:47 -0800)
committerAndrew DeFaria <Andrew.DeFaria@Broadcom.com>
Mon, 21 Nov 2016 23:47:08 +0000 (15:47 -0800)
audience/getPicture.conf [new file with mode: 0644]
audience/getPicture.pl [new file with mode: 0755]
bin/bice.py [new file with mode: 0644]
bin/rexec.new [new file with mode: 0755]
bin/test.py [new file with mode: 0644]
maps/test.msg [new file with mode: 0644]
maps/test.pl [new file with mode: 0644]
test/test.py [new file with mode: 0755]

diff --git a/audience/getPicture.conf b/audience/getPicture.conf
new file mode 100644 (file)
index 0000000..a04c3b3
--- /dev/null
@@ -0,0 +1,21 @@
+################################################################################
+#
+# File:         getPicture.conf
+# Description:  Configuration file for getPicture.pl
+# Author:       Andrew DeFaria <Andrew@ClearSCM.com>
+# Version:      1.0
+# Created:      Fri Oct  3 18:16:26 PDT 2014
+# Modified:     $Date: 2014/10/03 18:17:20 $
+# Language:     Conf
+#
+# This file contains configurable parameters that are unique to your site that
+# identify the LDAP parms needed to retrieve users pictures from Active 
+# Directory. It is assumed that thumbnailPhoto is the attribute name and that
+# the user can be found by their unique uid.
+#
+################################################################################
+AD_HOST:     <host name of your LDAP server>
+AD_PORT:     <LDAP Port - normally 389>
+AD_BINDDN:   <DN of user you have to use to bind to the LDAP server>
+AD_BINDPW:   <The bind user's password>
+AD_BASEDN:   <Base DN of where to start the search>
diff --git a/audience/getPicture.pl b/audience/getPicture.pl
new file mode 100755 (executable)
index 0000000..e74ee10
--- /dev/null
@@ -0,0 +1,202 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+=pod
+
+=head1 NAME getPicture.pl
+
+Retrieve thumbnailPhoto for the userid from Active Directory
+
+=head1 VERSION
+
+=over
+
+=item Author
+
+Andrew DeFaria <Andrew@ClearSCM.com>
+
+=item Revision
+
+$Revision: #8 $
+
+=item Created
+
+Fri Oct  3 18:16:26 PDT 2014
+
+=item Modified
+
+$Date: 2014/10/03 18:17:20 $
+
+=back
+
+=head1 DESCRIPTION
+
+This script will take a userid and search the Active Directory for the user and
+return an image file if the user has an image associated with his 
+thumbnailPhoto attribute.
+
+This can be configured into Perforce Swarn as documented:
+
+http://www.perforce.com/perforce/doc.current/manuals/swarm/admin.avatars.html
+
+One would use something like
+
+  // this block should be a peer of 'p4'
+  'avatars' => array(
+    'http_url'  => 'http://<server>/cgi-bin/getPicture.pl?userid={user}'
+    'https_url' => 'http://<server>/cgi-bin/getPicture.pl?userid={user}',
+  ),
+
+=cut
+
+use FindBin;
+use Getopt::Long;
+use Net::LDAP;
+use CGI qw (:standard);
+
+# Interpolate variable in str (if any) from %opts
+sub interpolate ($%) {
+  my ($str, %opts) = @_;
+
+  # Since we wish to leave undefined $var references in tact the following while
+  # loop would loop indefinitely if we don't change the variable. So we work
+  # with a copy of $str changing it always, but only changing the original $str
+  # for proper interpolations.
+  my $copyStr = $str;
+
+  while ($copyStr =~ /\$(\w+)/) {
+    my $var = $1;
+
+    if (exists $opts{$var}) {
+      $str     =~ s/\$$var/$opts{$var}/;
+      $copyStr =~ s/\$$var/$opts{$var}/;
+    } elsif (exists $ENV{$var}) {
+      $str     =~ s/\$$var/$ENV{$var}/;
+      $copyStr =~ s/\$$var/$ENV{$var}/;
+    } else {
+     $copyStr =~ s/\$$var//;
+  } # if
+ } # while
+
+ return $str;
+} # interpolate
+
+sub _processFile ($%) {
+  my ($configFile, %opts) = @_;
+  
+  while (<$configFile>) {
+    chomp;
+
+    next if /^\s*[\#|\!]/;    # Skip comments
+
+    if (/\s*(.*?)\s*[:=]\s*(.*)\s*/) {
+      my $key   = $1;
+      my $value = $2;
+
+      # Strip trailing spaces
+      $value =~ s/\s+$//;
+
+      # Interpolate
+      $value = interpolate $value, %opts;
+
+      if ($opts{$key}) {
+        # If the key exists already then we have a case of multiple values for 
+        # the same key. Since we support this we need to replace the scalar
+        # value with an array of values...
+        if (ref $opts{$key} eq "ARRAY") {
+          # It's already an array, just add to it!
+          push @{$opts{$key}}, $value;
+        } else {
+          # It's not an array so make it one
+          my @a;
+
+          push @a, $opts{$key};
+          push @a, $value;
+          $opts{$key} = \@a;
+        } # if
+      } else {
+        # It's a simple value
+        $opts{$key} = $value;
+      }  # if
+    } # if
+  } # while
+  
+  return %opts;
+} # _processFile
+
+sub GetConfig ($) {
+  my ($filename) = @_;
+
+  my %opts;
+
+  open my $configFile, '<', $filename
+    or die "Unable to open config file $filename";
+
+  %opts = _processFile $configFile;
+
+  close $configFile;
+
+  return %opts;
+} # GetConfig
+
+sub checkLDAPError ($$) {
+  my ($msg, $result) = @_;
+  
+  my $code = $result->code;
+  
+  die "$msg (Error $code)\n" . $result->error if $code;
+} # checkLDAPError
+
+my ($confFile) = ($FindBin::Script =~ /(.*)\.pl$/);
+    $confFile = "$confFile.conf";
+
+my %opts = GetConfig ($confFile);
+
+## Main
+$| = 1;
+
+GetOptions (
+  \%opts,
+  'AD_HOST=s',
+  'AD_PORT=s',
+  'AD_BINDDN=s',
+  'AD_BINDPW=s',
+  'AD_BASEDN=s',
+  'userid=s', 
+) || die 'Invalid parameters';
+
+$opts{userid} = param 'userid' unless $opts{userid};
+
+die "Usage getPicture.pl [userid=]<userid>\n" unless $opts{userid};
+
+my $ldap = Net::LDAP->new (
+  $opts{AD_HOST}, (
+    host   => $opts{AD_HOST},
+    port   => $opts{AD_PORT},
+    basedn => $opts{AD_BASEDN},
+    binddn => $opts{AD_BINDDN},
+    bindpw => $opts{AD_BINDPW},
+  ),
+) or die $@;
+
+my $result = $ldap->bind (
+  dn       => $opts{AD_BINDDN},
+  password => $opts{AD_BINDPW},
+) or die "Unable to bind\n$@";
+
+checkLDAPError ('Unable to bind', $result);
+
+$result = $ldap->search (
+  base   => $opts{AD_BASEDN},
+  filter => "uid=$opts{userid}",
+);
+
+checkLDAPError ('Unable to search', $result);
+
+my @entries = ($result->entries);
+
+if ($entries[0]) {
+  print header 'image/jpeg';
+  print $entries[0]->get_value ('thumbnailPhoto');  
+} # if
diff --git a/bin/bice.py b/bin/bice.py
new file mode 100644 (file)
index 0000000..c5bb2f3
--- /dev/null
@@ -0,0 +1,153 @@
+#!/usr/bin/python
+
+import sys
+import getopt
+import fcntl
+import re
+
+from array import *
+
+security_logfile = '/var/log/auth.log'
+verbose = False
+debug   = False
+update  = False
+email   = False
+
+def Error (msg = '', errno = 0):
+  sys.stderr.write ('Error: ' + msg)
+
+  if (errno <> 0):  
+    sys.exit (errno)
+
+def Verbose (msg, linefeed = True):
+  global verbose
+  
+  if (linefeed):
+    msg += '\n'
+    
+  if (verbose):
+    print msg
+
+def Usage (msg = ''):
+  if msg != '':
+    print msg
+    
+  print """
+Usage: bice.py [-u|sage] [-v|erbose] [-d|ebug] [-nou|pdate] [-nom|ail]
+               [-f|ilename <filename> ]
+
+Where:
+  -u|sage     Print this usage
+  -v|erbose:  Verbose mode (Default: -verbose)
+  -nou|pdate: Don't update security logfile file (Default: -update)
+  -nom|ail:   Don't send emails (Default: -mail)
+  -f|ilename: Open alternate messages file (Default: /var/log/auth.log)
+  """
+  
+  sys.exit (1)
+
+def processLogfile (logfile):
+  violations = {}
+    
+  try:
+    readlog = open (logfile)
+
+    fcntl.flock (readlog, fcntl.LOCK_EX)
+  except IOError:
+    Error ("Unable to get exclusive access to " + logfile + " - $!", 1)
+    
+  invalid_user           = re.compile ("^(\S+\s+\S+\s+\S+)\s+.*Invalid user (\w+) from (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})")
+  authentication_failure = re.compile ("^(\S+\s+\S+\s+\S+)\s+.*authentication failure.*ruser=(\S+).*rhost=(\S+)")
+  failed_password        = re.compile ("^(\S+\s+\S+\s+\S+)\s+.*Failed password for (\w+) from (\d{1,3}\.\d{1,3}\.d{1,3}\.d{1,3})")
+  
+  for newline in readlog:
+    violation = {}
+    newline = newline.strip ()
+    
+    timestamp = ''
+    user      = ''
+    ip        = ''
+    
+    for (timestamp, user, ip) in invalid_user.findall (newline):
+      continue
+      
+    for (timestamp, user, ip) in authentication_failure.findall (newline):
+      continue
+      
+    for (timestamp, user, ip) in failed_password.findall (newline):
+      continue
+     
+    if (ip == ''):
+      continue
+      
+    if (ip in violations):
+      violation = violations[ip]
+
+    if (user in violation):
+      violation[user].append (timestamp)
+    else:
+      violation[user] = [];
+      violation[user].append (timestamp)
+    
+    violations[ip] = violation
+    
+  return violations
+
+def ReportBreakins (logfile):
+  violations = processLogfile (logfile)
+  
+  nbrViolations = len (violations)
+  
+  if (nbrViolations == 0):
+    Verbose ('No violations found')
+  elif (nbrViolations == 1):
+    Verbose ('1 site attempting to violate our perimeter')
+  else:
+    Verbose ('{} violations'.format(nbrViolations))
+
+  for ip in violations:
+    print 'IP: ' + ip + ' violations:'
+    for key in sorted (violations[ip].iterkeys ()):
+      print "\t{}: {}".format (key, violations[ip][key])
+                    
+def dumpargs ():
+  global verbose, debug, update, email, security_logfile
+
+  print 'Args:'
+  print 'verbose', verbose
+  print 'debug',   debug
+  print 'update',  update
+  print 'email',   email
+  print 'file',    security_logfile
+
+def main (argv):
+  global verbose, debug, update, email, security_logfile
+  
+  try:
+    opts, args = getopt.getopt (argv, "vd", ['verbose', 'debug', 'usage', 'update', 'mail', 'file='])
+  except getopt.GetoptError:
+    Usage ();
+    sys.exit (1);
+
+  for opt, arg in opts:
+    if opt in ['-v', '--verbose']:
+      verbose = 1
+    elif opt in ['-d', '--debug']:
+      debug = 1
+    elif opt in ['-u', '--usage']:
+      Usage 
+    elif opt in ['--update']:
+      update = 1
+    elif opt in ['-m', '--mail']:
+      email = 1
+    elif opt in ['-f', '--file']:
+      security_logfile = arg
+  
+  if security_logfile == '':
+    Usage ('Must specify filename')
+    
+  ReportBreakins (security_logfile)
+#  dumpargs ()
+  
+if __name__ == '__main__':
+  main (sys.argv [1:])
\ No newline at end of file
diff --git a/bin/rexec.new b/bin/rexec.new
new file mode 100755 (executable)
index 0000000..016119e
--- /dev/null
@@ -0,0 +1,336 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+=pod
+
+=head1 NAME $RCSfile: rexec,v $
+
+Run arbitrary command on a set of machines
+
+=head1 VERSION
+
+=over
+
+=item Author
+
+Andrew DeFaria <Andrew@ClearSCM.com>
+
+=item Revision
+
+$Revision: 1.0 $
+
+=item Created:
+
+Tue Jan  8 15:57:27 MST 2008
+
+=item Modified:
+
+$Date: 2008/02/29 15:09:15 $
+
+=back
+
+=head1 SYNOPSIS
+
+ Usage: rexec [-u|sage] [-v|erbose] [-d|ebug] [-t|ype <machine type>]
+              <command>
+
+ Where:
+   -u|sage     Print this usage
+   -v|erbose:  Verbose mode
+   -d|ebug:    Print debug messages
+   -t|ype:     Machine type (Linux, Windows)
+   <command>:  Command to execute
+
+=head1 DESCRIPTION
+
+This script will perform and arbitrary command on a set of machines.
+
+=cut
+
+use FindBin;
+use Getopt::Long;
+use Pod::Usage;
+use Term::ANSIColor qw(:constants);
+use POSIX ":sys_wait_h";
+
+use lib "$FindBin::Bin/../lib", "$FindBin::Bin/../clearadm/lib";
+
+use Display;
+use Clearadm;
+use Logger;
+use Rexec;
+use Utils;
+
+my %total;
+my ($currentHost, $skip, $log);
+
+my %opts = (
+  usage    => sub { pod2usage },
+  help     => sub { pod2usage (-verbose => 2)},
+  verbose  => sub { set_verbose },
+  debug    => sub { set_debug },
+  parallel => 0,
+);
+
+my (%workerStatuses, %workerNames);
+
+sub Interrupted {
+  use Term::ReadKey;
+
+  display BLUE . "\nInterrupted execution on $currentHost" . RESET;
+
+  Stats \%total;
+
+  display_nolf "Executing on " . YELLOW . $currentHost . RESET . " - "
+    . GREEN     . BOLD . "S" . RESET . GREEN    . "kip"         . RESET . ", "
+    . CYAN      . BOLD . "C" . RESET . CYAN     . "ontinue"     . RESET . " or "
+    . MAGENTA   . BOLD . "A" . RESET . MAGENTA  . "bort run"    . RESET . " ("
+    . GREEN     . BOLD . "s" . RESET . "/"
+    . CYAN      . BOLD . "C" . RESET . "/"
+    . MAGENTA   . BOLD . "a" . RESET . ")?";
+
+  ReadMode ("cbreak");
+  my $answer = ReadKey (0);
+  ReadMode ("normal");
+
+  if ($answer eq "\n") {
+    display "c";
+  } else {
+    display $answer;
+  } # if
+
+  $answer = lc $answer;
+
+  if ($answer eq "s") {
+    *STDOUT->flush;
+    display "Skipping $currentHost";
+    $skip = 1;
+    $total{Skips}++;
+  } elsif ($answer eq "a") {
+    display RED . "Aborting run". RESET;
+    Stats \%total;
+    exit;
+  } else {
+    display "Continuing...";
+    $skip = 0;
+  } # if
+  
+  return;
+} # Interrupted
+
+sub workerDeath {
+  while ((my $worker = waitpid (-1, WNOHANG)) > 0) {
+    my $status  = $?;
+
+    # Ignore all child deaths except for processes we started
+    next if !exists $workerStatuses{$worker};
+
+    $workerStatuses{$worker} = $status;
+  } # while
+
+  $SIG{CHLD} = \&workerDeath;
+  
+  return;
+} # workerDeath
+
+sub execute ($$;$) {
+  my ($cmd, $host, $prompt) = @_;
+
+  my ($remoteHost, @lines);
+
+  # Mark $currentHost for interrupt
+  $currentHost = $host;
+  
+  # Start a log...
+  $log = Logger->new (name => $host) if $opts{log};
+  
+  verbose_nolf "Connecting to machine $host...";
+
+  display_nolf YELLOW . "$host:" . RESET;
+
+  eval {
+    $remoteHost = Rexec->new (
+      host   => $host,
+      prompt => $prompt,
+    );
+  };
+
+  # Problem with creating Rexec object. Log error if logging and return.
+  if ($@ || !$remoteHost) {
+    if ($opts{log}) {
+      $log->err ("Unable to connect to $host to execute command: $cmd") if $opts{log};
+    } else {
+      display RED . 'ERROR:' . RESET . " Unable to connect to $host to execute command: $cmd";
+    } # if
+
+    $total{ConnectFailures}++;
+
+    return (1, ());
+  } # if
+
+  verbose " connected";
+
+  display UNDERLINE . "$cmd" . RESET unless $opts{quiet};
+
+  @lines = $remoteHost->execute ($cmd);
+
+  if ($skip) {
+    # Kick current connection
+    kill INT => $remoteHost->{handle}->pid;
+  } # if
+
+#  if ($opts{parallel} != 0) {
+#    $log->err ("Unable to connect to $host to execute command\n$cmd") if $opts{log};
+#
+#    $total{ConnectFailures}++;
+#  } # if
+
+  verbose "Disconnected from $host";
+
+  my $status = $remoteHost->status;
+
+  return ($status, @lines);
+} # execute
+
+sub parallelize ($%) {
+  my ($cmd, %machines) = @_;
+
+  my $thread_count = 1;
+
+  foreach my $host (sort keys %machines) {
+    if ($thread_count <= $opts{parallel}) {
+      debug "Processing $host ($thread_count)";
+      $thread_count++;
+
+      if (my $pid = fork) {
+        # In parent process - record this host and its status
+        $workerNames{$pid} = $host;
+      } else {
+        # In spawned child...
+        $pid = $$;
+
+        debug "Starting process for $host [$pid]";
+
+        $workerNames{$pid} = $host;
+        
+        # Mark currentHost for interrupt (How does this work in the presence
+        # of parallelization?).
+        $currentHost = $host;
+       
+        my ($status, @lines) = execute $cmd, $host, $machines{$host};
+
+        $log = Logger->new (name => $host);
+
+        $log->log ($_) foreach (@lines);
+
+        exit $status;
+      } # if
+    } else {
+      # Wait for somebody to finish;
+      debug "Waiting for somebody to exit...";
+      my $reaped = wait;
+
+      debug "Reaped $workerNames{$reaped} [$reaped] (Status: $?)";
+      $workerStatuses{$reaped} = $? >> 8 if $reaped != -1;
+
+      $thread_count--;
+    } # if
+  } # foreach
+
+  # Wait for all kids
+  my %threads = %workerNames;
+
+  foreach (keys %threads) {
+    if (waitpid ($_, 0) == -1) {
+      delete $threads{$_};
+    } else {
+      $workerStatuses{$_} = $? >> 8;
+      debug "$threads{$_} [$_] exited with a status of $workerStatuses{$_}";
+    } # if
+  } # foreach
+
+  debug "All processed completed - Status:";
+
+  if (get_debug) {
+    foreach (sort keys %workerStatuses) {
+      debug "$workerNames{$_}\t[$_]:\tStatus: $workerStatuses{$_}";
+    } # foreach
+  } # if
+
+  # Gather output...
+  display "Output of all executions";
+  foreach my $host (sort keys %machines) {
+    if (-f "$host.log") {
+      display "$host:$_" foreach (ReadFile ("$host.log"));
+
+      #unlink "$_host.log";
+    } else {
+      warning "Unable to find output for $host ($host.log missing)";
+    } # if
+  } # foreach
+  
+  return;
+} # parallelize
+
+# Print the totals if interrupted
+$SIG{INT} = \&Interrupted;
+
+# Get our options
+GetOptions (
+  \%opts,
+  'usage',
+  'help',
+  'verbose',
+  'debug',
+  'log',
+  'quiet',
+  'type=s',
+  'parallel=i',
+);
+
+my $cmd = join ' ', @ARGV;
+
+pod2usage ('No command specified') unless $cmd;
+
+my $machines = Clearadm->new;
+
+if ($opts{parallel} > 0) {
+  #parallelize ($cmd, %machines);
+  Stats \%total;
+  exit;
+} # if
+
+display "NOTE: Logging outputs to <host>.log" if $opts{log};
+
+my $condition = $opts{type} ? "type=$opts{type}" : '';
+
+foreach ($machines->SearchSystem ($condition)) {
+  my %system = %$_;
+  $total{Machines}++;
+
+  my ($status, @lines) = execute $cmd, $system{name};
+
+  if ($skip) {
+    $skip = 0;
+    next;
+  } # if
+
+  if (defined $status) {
+    if ($status == 0) {
+      $total{Executions}++;
+    } else {
+      $total{Failures}++;
+
+      next;
+    } # if
+  } # if
+
+  if ($opts{log}) {
+    $log->log ($_) foreach (@lines);
+  } else {
+    display $_ foreach (@lines);
+  } # if
+} # foreach
+
+Stats \%total;
\ No newline at end of file
diff --git a/bin/test.py b/bin/test.py
new file mode 100644 (file)
index 0000000..f317064
--- /dev/null
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+
+import sys, getopt
+
+security_logfile = '/var/log/auth.log'
+verbose = 0
+debug   = 0
+update  = 0
+email   = 0
+
+def Usage (msg = ''):
+  if msg != '':
+    print msg
+    
+  print """
+Usage: bice.py [-u|sage] [-v|erbose] [-d|ebug] [-nou|pdate] [-nom|ail]
+               [-f|ilename <filename> ]
+
+Where:
+  -u|sage     Print this usage
+  -v|erbose:  Verbose mode (Default: -verbose)
+  -nou|pdate: Don't update security logfile file (Default: -update)
+  -nom|ail:   Don't send emails (Default: -mail)
+  -f|ilename: Open alternate messages file (Default: /var/log/auth.log)
+  """
+def mainold (argv):
+  global verbose, debug, update, email, security_logfile
+  
+  print "in main"
+  
+  try:
+    #opts, args = getopt.getopt (argv, "vd", ['verbose', 'debug', 'usage', 'update', 'mail', 'file='])
+    opts, args = getopt.getopt (argv, "hi:o:", ["ifile=","ofile="])
+  except getopt.GetoptError:
+    Usage;
+    sys.exit (1);
+    
+  for opt, arg in opts:
+    print 'opt: ', opt
+    print 'arg:',  arg
+     
+    if opt in ['-v', '-verbose']:
+      verbose = 1
+    elif opt in ['-d', '-debug']:
+      debug = 1
+    elif opt in ['-u', '-usage']:
+      Usage 
+    elif opt in ['-update']:
+      update = 1
+    elif opt in ['-m', '-mail']:
+      email = 1
+    elif opt in ['-f', '-file']:
+      security_logfile = arg
+  
+  print 'Args:'
+  print 'verbose', verbose
+  print 'debug',   debug
+  print 'update',  update
+  print 'email',   email
+  print 'file',    security_logfile
+  
+def main(argv):
+  global verbose, debug, update, email, security_logfile
+
+  inputfile = ''
+  outputfile = ''
+
+  try:
+    opts, args = getopt.getopt(argv,"hvi:o:",["verbose", "ifile=","ofile="])
+  except getopt.GetoptError:
+    Usage ()
+    print 'test.py -i <inputfile> -o <outputfile>'
+    sys.exit(2)
+    
+  for opt, arg in opts:
+    print 'opt: ', opt
+    print 'arg: ', arg
+    if opt in ['-v', '--verbose']:
+      verbose = 1
+    elif opt in ['-u', '-usage']:
+      Usage ()
+    elif opt == '-h':
+      print 'test.py -i <inputfile> -o <outputfile>'
+      sys.exit()
+    elif opt in ("-i", "--ifile"):
+      inputfile = arg
+    elif opt in ("-o", "--ofile"):
+      outputfile = arg
+
+  print 'Args:'
+  print 'verbose', verbose
+  print 'debug',   debug
+  print 'update',  update
+  print 'email',   email
+  print 'file',    security_logfile
+
+if __name__ == "__main__":
+   main (sys.argv[1:])
\ No newline at end of file
diff --git a/maps/test.msg b/maps/test.msg
new file mode 100644 (file)
index 0000000..fe92463
--- /dev/null
@@ -0,0 +1,171 @@
+Message-ID: <546412AB.7090404@DeFaria.com>\r
+Date: Wed, 12 Nov 2014 18:08:43 -0800\r
+From: Andrew DeFaria <Andrew@DeFaria.com>\r
+User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Thunderbird/31.2.0\r
+MIME-Version: 1.0\r
+To: Yubico Support <support@yubico.com>\r
+Subject: Glutten for punishment...\r
+Content-Type: multipart/alternative;\r
+ boundary="------------060602060101040803010204"\r
+\r
+This is a multi-part message in MIME format.\r
+--------------060602060101040803010204\r
+Content-Type: text/plain; charset=utf-8; format=flowed\r
+Content-Transfer-Encoding: 7bit\r
+\r
+OK I returned the Yubico Security key thing and bought the Yubikey Neo. \r
+I'm on Windows 7 with Chrome  38.0.2125.111 m. I even have the FIDO U2F \r
+(Universal 2nd Factor) extension 0.9.3 installed.\r
+\r
+I go to http://demo.yubico.com/start/u2f/neo?tab=register and insert my \r
+key. Unlike the instructions my yubikey is not a "flashing U2F device" - \r
+the light is on solid. I hit the button anyway. It does nothing but \r
+eventually times out with:\r
+\r
+\r
+        Registration failed!\r
+\r
+    Make sure you have a U2F device connected, and try again.\r
+\r
+      Traceback (most recent call last):\r
+       File "/root/python-u2flib-server-demo/examples/yubiauth_server.py", line 159, in __call__\r
+         raise Exception("FIDO Client error: %s" % error)\r
+    Exception: FIDO Client error: 5 (TIMEOUT)\r
+      \r
+\r
+/root? Really?\r
+\r
+Similarly when I go to my Google account under Security Keys and follow \r
+the instruction there the yubikey doesn't do anything! When I tap the \r
+gold circle the light goes out for a brief second then back on. But the \r
+"Now insert (and tap) your Security Key" with the spinning progress \r
+indicator goes forever...\r
+\r
+Now what?\r
+-- \r
+Andrew DeFaria <http://defaria.com>\r
+ClearSCM, Inc. <http://clearscm.com>\r
+\r
+--------------060602060101040803010204\r
+Content-Type: text/html; charset=utf-8\r
+Content-Transfer-Encoding: 8bit\r
+\r
+<html>\r
+  <head>\r
+\r
+    <meta http-equiv="content-type" content="text/html; charset=utf-8">\r
+  </head>\r
+  <body style="background-color: rgb(255, 255, 255); color: rgb(0, 0,\r
+    0);" bgcolor="#FFFFFF" text="#000000">\r
+    OK I returned the Yubico Security key thing and bought the Yubikey\r
+    Neo. I'm on Windows 7 with Chrome  38.0.2125.111 m. I even have the\r
+    FIDO U2F (Universal 2nd Factor) extension 0.9.3 installed. <br>\r
+    <br>\r
+    I go to <a class="moz-txt-link-freetext" href="http://demo.yubico.com/start/u2f/neo?tab=register">http://demo.yubico.com/start/u2f/neo?tab=register</a> and insert\r
+    my key. Unlike the instructions my yubikey is not a "flashing U2F\r
+    device" - the light is on solid. I hit the button anyway. It does\r
+    nothing but eventually times out with:<br>\r
+    <blockquote>\r
+      <h2 style="margin: 10px 0px; font-family: 'Helvetica Neue',\r
+        Helvetica, Arial, sans-serif; font-weight: bold; line-height:\r
+        40px; color: rgb(51, 51, 51); text-rendering:\r
+        optimizelegibility; font-size: 31.5px; font-style: normal;\r
+        font-variant: normal; letter-spacing: normal; orphans: auto;\r
+        text-align: start; text-indent: 0px; text-transform: none;\r
+        white-space: normal; widows: auto; word-spacing: 0px;\r
+        -webkit-text-stroke-width: 0px;">Registration failed!</h2>\r
+      <p style="margin: 0px 0px 10px; color: rgb(51, 51, 51);\r
+        font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;\r
+        font-size: 18px; font-style: normal; font-variant: normal;\r
+        font-weight: 200; letter-spacing: normal; line-height: 30px;\r
+        orphans: auto; text-align: start; text-indent: 0px;\r
+        text-transform: none; white-space: normal; widows: auto;\r
+        word-spacing: 0px; -webkit-text-stroke-width: 0px;">Make sure\r
+        you have a U2F device connected, and try again.</p>\r
+      <pre style="padding: 9.5px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 13px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; display: block; margin: 0px 0px 10px; line-height: 20px; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; border: 1px solid rgba(0, 0, 0, 0.14902); font-style: normal; font-variant: normal; font-weight: 200; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(245, 245, 245);"> Traceback (most recent call last):\r
+  File "/root/python-u2flib-server-demo/examples/yubiauth_server.py", line 159, in __call__\r
+    raise Exception("FIDO Client error: %s" % error)\r
+Exception: FIDO Client error: 5 (TIMEOUT)\r
+ </pre>\r
+    </blockquote>\r
+    <div class="moz-signature">/root? Really?<br>\r
+      <br>\r
+      Similarly when I go to my Google account under Security Keys and\r
+      follow the instruction there the yubikey doesn't do anything! When\r
+      I tap the gold circle the light goes out for a brief second then\r
+      back on. But the "Now insert (and tap) your Security Key" with the\r
+      spinning progress indicator goes forever...<br>\r
+      <br>\r
+      Now what?<br>\r
+      -- <br>\r
+      <style type="text/css">\r
+body {\r
+  font:                        Helvetica, Arial, sans-serif;\r
+}\r
+p {\r
+  font:                        Helvetica, Arial, sans-serif;\r
+}\r
+.standout {\r
+  font-family:         verdana,\r
+                       arial,\r
+                       sans-serif;\r
+  font-size:           12px;\r
+  color:               #993333;\r
+  line-height:         13px;\r
+  font-weight:         bold;\r
+  margin-bottom:       10px;\r
+}\r
+.code {\r
+  border-top:          1px solid #ddd;\r
+  border-left:         1px solid #ddd;\r
+  border-right:                2px solid #000;\r
+  border-bottom:       2px solid #000;\r
+  padding:             10px;\r
+  margin-top:          5px;\r
+  margin-left:         5%;\r
+  margin-right:                5%;\r
+  background:          #ffffea;\r
+  color:               black;\r
+  font-family:         courier;\r
+  white-space:         pre;\r
+  -moz-border-radius:  10px;\r
+}\r
+.terminal {\r
+  border-top:          10px solid #03f;\r
+  border-left:         1px solid #ddd;\r
+  border-right:                2px solid grey;\r
+  border-bottom:       2px solid grey;\r
+  padding:             10px;\r
+  margin-top:          5px;\r
+  margin-left:         5%;\r
+  margin-right:                5%;\r
+  background:          black;\r
+  color:               white;\r
+  font-family:         courier;\r
+  white-space:         pre;\r
+  -moz-border-radius:  10px;\r
+}\r
+a:link { \r
+  color:               blue;\r
+}\r
+\r
+a:visited {\r
+  color:               darkblue;\r
+}\r
+\r
+a:hover { \r
+  color:               black;\r
+  background-color:    #ffffcc;\r
+  text-decoration:     underline;\r
+}\r
+\r
+a:active { \r
+  color:               red;\r
+}\r
+</style><a href="http://defaria.com">Andrew DeFaria</a><br>\r
+      <a href="http://clearscm.com">ClearSCM, Inc.</a><br>\r
+    </div>\r
+  </body>\r
+</html>\r
+\r
+--------------060602060101040803010204--\r
diff --git a/maps/test.pl b/maps/test.pl
new file mode 100644 (file)
index 0000000..0310205
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use lib "/opt/clearscm/lib";
+
+use Display;
+use Utils;
+use Net::SMTP;
+
+my %config = (
+  SMTPHOST => 'defaria.com',
+);
+
+sub DeliverMsg ($) {
+  my ($msg) = @_;
+  
+  my $smtp = Net::SMTP->new ($config{SMTPHOST})
+    or error "Unable to connect to mail server: $config{SMTPHOST}", 1;
+  
+  $smtp->mail ('Andrew@DeFaria.com');
+  
+  $smtp->to ('adefaria@gmail.com');
+  
+  $smtp->data;
+  $smtp->datasend ('From: Andrew@DeFaria.com');
+  $smtp->datasend ('To: adefaria@gmail.com');
+  $smtp->datasend ('Subject: Forwarded mail');
+  $smtp->datasend ('Content-Type: plain/text');
+  $smtp->datasend ("\n");
+  $smtp->datasend ($msg);
+  $smtp->dataend;
+  $smtp->quit;
+  
+  return;    
+} # DeliverMsg
+
+my $msg = ReadFile 'test.msg';
+
+DeliverMsg ($msg);
+
+display 'done';
\ No newline at end of file
diff --git a/test/test.py b/test/test.py
new file mode 100755 (executable)
index 0000000..981ea9b
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+a, b = 0, 1
+
+while b < 100:
+  print b
+  a, b = b, a+b
+    
+print "done"
+
+quit()