Radix cross Linux

The main Radix cross Linux repository contains the build scripts of packages, which have the most complete and common functionality for desktop machines

452 Commits   2 Branches   1 Tag
Index: create.patch.sh
===================================================================
--- create.patch.sh	(nonexistent)
+++ create.patch.sh	(revision 5)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=5.9
+
+tar --files-from=file.list -xzvf ../net-snmp-$VERSION.tar.gz
+mv net-snmp-$VERSION net-snmp-$VERSION-orig
+
+cp -rf ./net-snmp-$VERSION-new ./net-snmp-$VERSION
+
+diff --unified -Nr  net-snmp-$VERSION-orig  net-snmp-$VERSION > net-snmp-$VERSION-cert-path.patch
+
+mv net-snmp-$VERSION-cert-path.patch ../patches
+
+rm -rf ./net-snmp-$VERSION
+rm -rf ./net-snmp-$VERSION-orig

Property changes on: create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: file.list
===================================================================
--- file.list	(nonexistent)
+++ file.list	(revision 5)
@@ -0,0 +1 @@
+net-snmp-5.9/local/net-snmp-cert
Index: net-snmp-5.9-new/local/net-snmp-cert
===================================================================
--- net-snmp-5.9-new/local/net-snmp-cert	(nonexistent)
+++ net-snmp-5.9-new/local/net-snmp-cert	(revision 5)
@@ -0,0 +1,2689 @@
+#!/usr/bin/perl
+
+use strict;
+use 5.8.0;
+
+use Getopt::Long qw(:config gnu_getopt no_ignore_case pass_through);
+use Cwd qw(getcwd realpath);
+use File::Basename;
+use File::Copy;
+use File::Path;
+use Sys::Hostname;
+use Carp;
+
+our @CC = (
+["AF","AFGHANISTAN"],
+["AX","ÅLAND ISLANDS"],
+["AL","ALBANIA"],
+["DZ","ALGERIA"],
+["AS","AMERICAN SAMOA"],
+["AD","ANDORRA"],
+["AO","ANGOLA"],
+["AI","ANGUILLA"],
+["AQ","ANTARCTICA"],
+["AG","ANTIGUA AND BARBUDA"],
+["AR","ARGENTINA"],
+["AM","ARMENIA"],
+["AW","ARUBA"],
+["AU","AUSTRALIA"],
+["AT","AUSTRIA"],
+["AZ","AZERBAIJAN"],
+["BS","BAHAMAS"],
+["BH","BAHRAIN"],
+["BD","BANGLADESH"],
+["BB","BARBADOS"],
+["BY","BELARUS"],
+["BE","BELGIUM"],
+["BZ","BELIZE"],
+["BJ","BENIN"],
+["BM","BERMUDA"],
+["BT","BHUTAN"],
+["BO","BOLIVIA, PLURINATIONAL STATE OF"],
+["BA","BOSNIA AND HERZEGOVINA"],
+["BW","BOTSWANA"],
+["BV","BOUVET ISLAND"],
+["BR","BRAZIL"],
+["IO","BRITISH INDIAN OCEAN TERRITORY"],
+["BN","BRUNEI DARUSSALAM"],
+["BG","BULGARIA"],
+["BF","BURKINA FASO"],
+["BI","BURUNDI"],
+["KH","CAMBODIA"],
+["CM","CAMEROON"],
+["CA","CANADA"],
+["CV","CAPE VERDE"],
+["KY","CAYMAN ISLANDS"],
+["CF","CENTRAL AFRICAN REPUBLIC"],
+["TD","CHAD"],
+["CL","CHILE"],
+["CN","CHINA"],
+["CX","CHRISTMAS ISLAND"],
+["CC","COCOS (KEELING) ISLANDS"],
+["CO","COLOMBIA"],
+["KM","COMOROS"],
+["CG","CONGO"],
+["CD","CONGO, THE DEMOCRATIC REPUBLIC OF THE"],
+["CK","COOK ISLANDS"],
+["CR","COSTA RICA"],
+["CI","CÔTE D'IVOIRE"],
+["HR","CROATIA"],
+["CU","CUBA"],
+["CY","CYPRUS"],
+["CZ","CZECH REPUBLIC"],
+["DK","DENMARK"],
+["DJ","DJIBOUTI"],
+["DM","DOMINICA"],
+["DO","DOMINICAN REPUBLIC"],
+["EC","ECUADOR"],
+["EG","EGYPT"],
+["SV","EL SALVADOR"],
+["GQ","EQUATORIAL GUINEA"],
+["ER","ERITREA"],
+["EE","ESTONIA"],
+["ET","ETHIOPIA"],
+["FK","FALKLAND ISLANDS (MALVINAS)"],
+["FO","FAROE ISLANDS"],
+["FJ","FIJI"],
+["FI","FINLAND"],
+["FR","FRANCE"],
+["GF","FRENCH GUIANA"],
+["PF","FRENCH POLYNESIA"],
+["TF","FRENCH SOUTHERN TERRITORIES"],
+["GA","GABON"],
+["GM","GAMBIA"],
+["GE","GEORGIA"],
+["DE","GERMANY"],
+["GH","GHANA"],
+["GI","GIBRALTAR"],
+["GR","GREECE"],
+["GL","GREENLAND"],
+["GD","GRENADA"],
+["GP","GUADELOUPE"],
+["GU","GUAM"],
+["GT","GUATEMALA"],
+["GG","GUERNSEY"],
+["GN","GUINEA"],
+["GW","GUINEA-BISSAU"],
+["GY","GUYANA"],
+["HT","HAITI"],
+["HM","HEARD ISLAND AND MCDONALD ISLANDS"],
+["VA","HOLY SEE (VATICAN CITY STATE)"],
+["HN","HONDURAS"],
+["HK","HONG KONG"],
+["HU","HUNGARY"],
+["IS","ICELAND"],
+["IN","INDIA"],
+["ID","INDONESIA"],
+["IR","IRAN, ISLAMIC REPUBLIC OF"],
+["IQ","IRAQ"],
+["IE","IRELAND"],
+["IM","ISLE OF MAN"],
+["IL","ISRAEL"],
+["IT","ITALY"],
+["JM","JAMAICA"],
+["JP","JAPAN"],
+["JE","JERSEY"],
+["JO","JORDAN"],
+["KZ","KAZAKHSTAN"],
+["KE","KENYA"],
+["KI","KIRIBATI"],
+["KP","KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF"],
+["KR","KOREA, REPUBLIC OF"],
+["KW","KUWAIT"],
+["KG","KYRGYZSTAN"],
+["LA","LAO PEOPLE'S DEMOCRATIC REPUBLIC"],
+["LV","LATVIA"],
+["LB","LEBANON"],
+["LS","LESOTHO"],
+["LR","LIBERIA"],
+["LY","LIBYAN ARAB JAMAHIRIYA"],
+["LI","LIECHTENSTEIN"],
+["LT","LITHUANIA"],
+["LU","LUXEMBOURG"],
+["MO","MACAO"],
+["MK","MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF"],
+["MG","MADAGASCAR"],
+["MW","MALAWI"],
+["MY","MALAYSIA"],
+["MV","MALDIVES"],
+["ML","MALI"],
+["MT","MALTA"],
+["MH","MARSHALL ISLANDS"],
+["MQ","MARTINIQUE"],
+["MR","MAURITANIA"],
+["MU","MAURITIUS"],
+["YT","MAYOTTE"],
+["MX","MEXICO"],
+["FM","MICRONESIA, FEDERATED STATES OF"],
+["MD","MOLDOVA, REPUBLIC OF"],
+["MC","MONACO"],
+["MN","MONGOLIA"],
+["ME","MONTENEGRO"],
+["MS","MONTSERRAT"],
+["MA","MOROCCO"],
+["MZ","MOZAMBIQUE"],
+["MM","MYANMAR"],
+["NA","NAMIBIA"],
+["NR","NAURU"],
+["NP","NEPAL"],
+["NL","NETHERLANDS"],
+["AN","NETHERLANDS ANTILLES"],
+["NC","NEW CALEDONIA"],
+["NZ","NEW ZEALAND"],
+["NI","NICARAGUA"],
+["NE","NIGER"],
+["NG","NIGERIA"],
+["NU","NIUE"],
+["NF","NORFOLK ISLAND"],
+["MP","NORTHERN MARIANA ISLANDS"],
+["NO","NORWAY"],
+["OM","OMAN"],
+["PK","PAKISTAN"],
+["PW","PALAU"],
+["PS","PALESTINIAN TERRITORY, OCCUPIED"],
+["PA","PANAMA"],
+["PG","PAPUA NEW GUINEA"],
+["PY","PARAGUAY"],
+["PE","PERU"],
+["PH","PHILIPPINES"],
+["PN","PITCAIRN"],
+["PL","POLAND"],
+["PT","PORTUGAL"],
+["PR","PUERTO RICO"],
+["QA","QATAR"],
+["RE","RÉUNION"],
+["RO","ROMANIA"],
+["RU","RUSSIAN FEDERATION"],
+["RW","RWANDA"],
+["BL","SAINT BARTHÉLEMY"],
+["SH","SAINT HELENA, ASCENSION AND TRISTAN DA CUNHA"],
+["KN","SAINT KITTS AND NEVIS"],
+["LC","SAINT LUCIA"],
+["MF","SAINT MARTIN"],
+["PM","SAINT PIERRE AND MIQUELON"],
+["VC","SAINT VINCENT AND THE GRENADINES"],
+["WS","SAMOA"],
+["SM","SAN MARINO"],
+["ST","SAO TOME AND PRINCIPE"],
+["SA","SAUDI ARABIA"],
+["SN","SENEGAL"],
+["RS","SERBIA"],
+["SC","SEYCHELLES"],
+["SL","SIERRA LEONE"],
+["SG","SINGAPORE"],
+["SK","SLOVAKIA"],
+["SI","SLOVENIA"],
+["SB","SOLOMON ISLANDS"],
+["SO","SOMALIA"],
+["ZA","SOUTH AFRICA"],
+["GS","SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS"],
+["ES","SPAIN"],
+["LK","SRI LANKA"],
+["SD","SUDAN"],
+["SR","SURINAME"],
+["SJ","SVALBARD AND JAN MAYEN"],
+["SZ","SWAZILAND"],
+["SE","SWEDEN"],
+["CH","SWITZERLAND"],
+["SY","SYRIAN ARAB REPUBLIC"],
+["TW","TAIWAN, PROVINCE OF CHINA"],
+["TJ","TAJIKISTAN"],
+["TZ","TANZANIA, UNITED REPUBLIC OF"],
+["TH","THAILAND"],
+["TL","TIMOR-LESTE"],
+["TG","TOGO"],
+["TK","TOKELAU"],
+["TO","TONGA"],
+["TT","TRINIDAD AND TOBAGO"],
+["TN","TUNISIA"],
+["TR","TURKEY"],
+["TM","TURKMENISTAN"],
+["TC","TURKS AND CAICOS ISLANDS"],
+["TV","TUVALU"],
+["UG","UGANDA"],
+["UA","UKRAINE"],
+["AE","UNITED ARAB EMIRATES"],
+["GB","UNITED KINGDOM"],
+["US","UNITED STATES"],
+["UM","UNITED STATES MINOR OUTLYING ISLANDS"],
+["UY","URUGUAY"],
+["UZ","UZBEKISTAN"],
+["VU","VANUATU"],
+["VE","VENEZUELA, BOLIVARIAN REPUBLIC OF"],
+["VN","VIET NAM"],
+["VG","VIRGIN ISLANDS, BRITISH"],
+["VI","VIRGIN ISLANDS, U.S."],
+["WF","WALLIS AND FUTUNA"],
+["EH","WESTERN SAHARA"],
+["YE","YEMEN"],
+["ZM","ZAMBIA"],
+["ZW","ZIMBABWE"],
+);
+
+package NetSNMP::Term;
+# gratefully taken from Wayne Thompson's Term::Complete
+# if newer CORE modules could be used this could be removed
+our($complete, $exit, $done, $kill, $erase1, $erase2, $tty_raw_noecho, $tty_restore, $stty, $tty_safe_restore);
+our($tty_saved_state) = '';
+CONFIG: {
+  $exit   = "\003";
+  $done   = "\004";
+  $kill   = "\025";
+  $erase1 =   "\177";
+  $erase2 =   "\010";
+  foreach my $s (qw(/bin/stty /usr/bin/stty)) {
+    if (-x $s) {
+      $tty_raw_noecho = "$s raw -echo";
+      $tty_restore    = "$s -raw echo";
+      $tty_safe_restore = $tty_restore;
+      $stty = $s;
+      last;
+    }
+  }
+}
+
+sub Complete {
+  my($prompt, $dflt, $help, $match, @cmp_lst);
+  my (%help, $cmp, $test, $l, @match);
+  my ($return, $r, $exitting) = ("", 0, 0);
+  my $tab;
+
+  $prompt = shift;
+  $dflt = shift;
+  $help = shift;
+  $match = shift;
+
+  if (ref $_[0] and ref($_[0][0])) {
+    @cmp_lst = @{$_[0]};
+  } else {
+    @cmp_lst = @_;
+  }
+  @cmp_lst = map {if (ref($_)) { $help{$_->[0]}=$_->[1]; $_->[0]} else {$_;}} @cmp_lst;
+
+  # Attempt to save the current stty state, to be restored later
+  if (defined $stty && defined $tty_saved_state && $tty_saved_state eq '') {
+    $tty_saved_state = qx($stty -g 2>/dev/null);
+    if ($?) {
+      # stty -g not supported
+      $tty_saved_state = undef;
+    } else {
+      $tty_saved_state =~ s/\s+$//g;
+      $tty_restore = qq($stty "$tty_saved_state" 2>/dev/null);
+    }
+  }
+  system $tty_raw_noecho if defined $tty_raw_noecho;
+ LOOP: {
+    local $_;
+    print($prompt, $return);
+    while (($_ = getc(STDIN)) ne "\r") {
+    CASE: {
+	# (TAB) attempt completion
+	$_ eq "\t" && do {
+	  if ($tab) {
+	    print(join("\r\n", '', map {exists $help{$_} ? sprintf("\t%-10.10s - %s", $_, $help{$_}) :
+					  "\t$_"} grep(/^\Q$return/, @cmp_lst)), "\r\n");
+	    $tab--;
+	    redo LOOP;
+	  }
+	  @match = grep(/^\Q$return/, @cmp_lst);
+	  unless ($#match < 0) {
+	    $l = length($test = shift(@match));
+	    foreach $cmp (@match) {
+	      until (substr($cmp, 0, $l) eq substr($test, 0, $l)) {
+		$l--;
+	      }
+	    }
+	    print("\a");
+	    print($test = substr($test, $r, $l - $r));
+	    $r = length($return .= $test);
+	  }
+	  $tab++;
+	  last CASE;
+	};
+
+	# (^C) exit
+	$_ eq $exit && do {
+	  print("\r\naborting application...\r\n");
+	  $exitting++;
+	  undef $return;
+	  last LOOP;
+	};
+
+	# (^D) done
+	$_ eq $done && do {
+	  undef $return;
+	  last LOOP;
+	};
+
+	# (?) show help if available
+	$_ eq '?' && do {
+	  print("\r\n$help\r\n");
+	  if (exists $help{$return}) {
+	    print("\t$return - $help{$return}\r\n");
+	  } else {
+	    print(join("\r\n", map {exists $help{$_} ? "\t$_ - $help{$_}" :
+				      "\t$_"} grep(/^\Q$return/, @cmp_lst)), "\r\n");
+	  }
+	  redo LOOP;
+	};
+
+	# (^U) kill
+	$_ eq $kill && do {
+	  if ($r) {
+	    $r	= 0;
+	    $return	= "";
+	    print("\r\n");
+	    redo LOOP;
+	  }
+	  last CASE;
+	};
+
+	# (DEL) || (BS) erase
+	($_ eq $erase1 || $_ eq $erase2) && do {
+	  if ($r) {
+	    print("\b \b");
+	    chop($return);
+	    $r--;
+	  }
+	  last CASE;
+	};
+
+	# printable char
+	ord >= 32 && do {
+	  $return .= $_;
+	  $r++;
+	  print;
+	  last CASE;
+	};
+      }
+    }
+
+    if (defined $return) {
+      if (length($return) == 0 or $return eq $dflt) {
+	$return = $dflt;
+      } elsif ($match == $NetSNMP::Cert::MATCH and scalar(grep {/^$return$/} @cmp_lst) != 1 or
+	       $match == $NetSNMP::Cert::PREMATCH and scalar(grep {$return=~/$_/} @cmp_lst) != 1) {
+	$r	= 0;
+	$return	= "";
+	print("\r\nChoose a valid option, or ^D to exit\r\n");
+	redo LOOP;
+      }
+    }
+  }
+ DONE:
+  # system $tty_restore if defined $tty_restore;
+  if (defined $tty_saved_state && defined $tty_restore && defined $tty_safe_restore) {
+    system $tty_restore;
+    if ($?) {
+      # tty_restore caused error
+      system $tty_safe_restore;
+    }
+  }
+
+  exit(1) if $exitting;
+  print("\n");
+  return $return;
+}
+
+package NetSNMP::Cert;
+
+our $VERSION      = '0.2.9';
+
+our $PROG         = ::basename($0);
+our $DEBUG        = 0;
+our $OK           = 0;
+our $ERR          = -1;
+
+# user input param
+our $MATCH        = 1;
+our $PREMATCH     = 2;
+
+# Load LWP if possible to import cert from URL
+eval('use LWP::UserAgent;');
+our $haveUserAgent = ($@ ? 0 : 1);
+
+# required executables
+our $OPENSSL     = $ENV{NET_SNMP_CRT_OPENSSL}  || 'openssl';
+our $CFGTOOL     = $ENV{NET_SNMP_CRT_CFGTOOL}  || 'net-snmp-config';
+
+# default app config file
+our $CFGFILE     = $ENV{NET_SNMP_CRT_CFGFILE}  || 'net-snmp-cert.conf';
+
+# default OpenSSL config files
+our $SSLCFGIN    = $ENV{NET_SNMP_CRT_SSLCFGIN} || 'openssl.in';
+our $SSLCFGOUT   = $ENV{NET_SNMP_CRT_SSLCFGIN} || '.openssl.conf';
+our $SSLCFG      = $ENV{NET_SNMP_CRT_SSLCFG};
+
+# default cmd logs
+our $OUTLOG      = $ENV{NET_SNMP_CRT_OUTLOG}   || '.cmd.out.log';
+our $ERRLOG      = $ENV{NET_SNMP_CRT_ERRLOG}   || '.cmd.err.log';
+
+# default cert dirs
+our $TLSDIR      = $ENV{NET_SNMP_CRT_TLSDIR}   || 'tls';
+our $CRTDIR      = $ENV{NET_SNMP_CRT_CRTDIR}   || 'certs';
+our $NEWCRTDIR   = $ENV{NET_SNMP_CRT_NEWCRTDIR}|| 'newcerts';
+our $CADIR       = $ENV{NET_SNMP_CRT_CADIR}    || 'ca-certs';
+our $PRIVDIR     = $ENV{NET_SNMP_CRT_PRIVDIR}  || 'private';
+
+our $SERIAL      = $ENV{NET_SNMP_CRT_SERIAL}   || '.serial';
+our $INDEX       = $ENV{NET_SNMP_CRT_INDEX}    || '.index';
+
+our $DEFCADAYS   = $ENV{NET_SNMP_CRT_DEFCADAYS} || 1825;
+our $DEFCRTDAYS  = $ENV{NET_SNMP_CRT_DEFCRTDAYS}|| 365;
+
+our @TLSDIRS     = ($CRTDIR, $NEWCRTDIR, $CADIR, $PRIVDIR);
+
+our @CRTSUFFIXES = qw(.pem .csr .der .crt);
+
+our @X509FMTS = qw(text modulus serial subject_hash issuer_hash hash subject
+		    purpose issuer startdate enddate dates fingerprint C);
+
+sub dprint {
+  my $str = shift;
+  my $opts = shift;
+  my $dlevel = shift || 1;
+  my $flag = (defined $opts ? int($opts->{'debug'} >= $dlevel) : 1);
+  my ($pkg, $file, $line, $sub) = caller(1);
+  my ($pkg0, $file0, $line0) = caller(0);
+  print("${sub}():$line0: $str") if $flag;
+}
+
+sub dwarn {
+  my $str = shift;
+  my $opts = shift;
+  my $dlevel = shift || 1;
+  my $flag = (defined $opts ? $opts->{'debug'} >= $dlevel : 1);
+  my ($pkg, $file, $line, $sub) = caller(1);
+  my ($pkg0, $file0, $line0) = caller(0);
+  warn("${sub}():$line0: $str") if $flag;
+}
+
+sub vprint {
+  my $str = shift;
+  my $opts = shift;
+  my $flag = (defined $opts ? $opts->{'verbose'} : 1);
+  my $debug = (defined $opts ? $opts->{'debug'} : 1);
+  my ($pkg, $file, $line, $sub) = caller(1);
+  my ($pkg0, $file0, $line0) = caller(0);
+  $str = ($debug ? "${sub}():$line0: $str" : "$str");
+  print("$str") if $flag;
+}
+
+sub vwarn {
+  my $str = shift;
+  my $opts = shift;
+  my $flag = (defined $opts ? $opts->{'verbose'} : 1);
+  my ($pkg, $file, $line, $sub) = caller(1);
+  my ($pkg0, $file0, $line0) = caller(0);
+  warn("${sub}():$line0: $str") if $flag;
+}
+
+sub rsystem {
+  my $cmd = shift;
+  my $flag = shift;
+
+  # if not running as root try to use sudo
+  if ($>) {
+    $cmd = "sudo $flag $cmd";
+  } else {
+    if ($flag =~ /-b/) {
+      $cmd = "$cmd \&";
+    }
+  }
+  die("cmd failed($!): $cmd\n") if system("$cmd");
+}
+
+sub usystem {
+  my $cmd = shift;
+  my $opts = shift;
+  my $ret;
+
+  unlink $NetSNMP::Cert::OUTLOG if -e $NetSNMP::Cert::OUTLOG;
+  unlink $NetSNMP::Cert::ERRLOG if -e $NetSNMP::Cert::ERRLOG;
+
+  $ret = system("$cmd");
+
+  if ($ret) {
+    if ($opts->{'verbose'}) {
+      system("cat $NetSNMP::Cert::OUTLOG") if -r $NetSNMP::Cert::OUTLOG;
+      system("cat $NetSNMP::Cert::ERRLOG") if -r $NetSNMP::Cert::ERRLOG;
+    }
+    die("cmd failed($!): $cmd\n");
+  }
+}
+
+sub home_dir {
+  my $cdir = ::getcwd();
+
+  chdir("~");
+  my $dir = ::getcwd();
+  chdir($cdir);
+
+  return $dir;
+}
+
+sub find_bin_dir {
+  # This code finds the path to the currently invoked program and
+  my $dir;
+
+  $0 =~ m%^(([^/]*)(.*)/)([^/]+)$%;
+  if ($1) {
+    if ($2) {
+      # Invoked using a relative path.  CD there and use pwd.
+      $dir = `cd $1 && pwd`;
+      chomp $dir;
+    } else {
+      # Invoked using an absolute path; that's it!
+      $dir = $3;
+    }
+  } else {
+    # No path.  Look in PATH for the first instance.
+    foreach my $p (split /:/, $ENV{PATH}) {
+      $p ||= '.';
+      -x "$p/$4" or next;
+      $dir = $p;
+    }
+  }
+  $dir or die "Cannot locate program '$0'!";
+}
+
+sub fq_rel_path {
+  my $path = shift;
+  my $cwd = ::getcwd();
+  my $rdir = shift || $cwd;
+
+  chdir($rdir) or die("can't change directory: $rdir");
+  # get fully qualified path
+  if ($path !~ m|^/|) {
+    my $pwd = `pwd`; chomp $pwd;
+    $path = "$pwd/$path";
+    $path = ::realpath($path) if -e $path;
+  }
+  chdir($cwd) or die("can't change directory: $cwd");
+
+  return $path;
+}
+
+sub dir_empty
+  {
+    my $path = shift;
+    opendir(DIR, $path);
+    foreach (readdir(DIR)) {
+      next if /^\.\.?$/;
+      closedir(DIR);
+      return 0;
+    }
+    closedir(DIR);
+    return 1;
+  }
+
+sub make_dirs {
+  my $opts = shift;
+  my $dir = shift;
+  my $mode = shift;
+  my @dirs = @_;
+
+  my $wd = ::getcwd();
+
+  NetSNMP::Cert::dprint("make dirs [$dir:@dirs] from $wd\n", $opts);
+
+  ::mkpath($dir, $opts->{'debug'}, $mode) or die("error - can't make $dir")
+    if defined $dir and not -d $dir;
+
+  foreach my $subdir (@dirs) {
+    my $d = "$subdir";
+    $d = "$dir/$d" if defined $dir;
+    NetSNMP::Cert::dprint("making directory: $d\n", $opts) unless -d $d;
+    mkdir($d, $mode) or die("error - can't make $d") unless -d $d;
+  }
+}
+
+sub is_url {
+  my $url = shift;
+
+  return $url =~ /^(?#Protocol)(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?#Username:Password)(?:\w+:\w+@)?(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?$/;
+}
+
+sub in_set {
+  my $elem = shift;
+  my $set = shift;
+  for my $e (eval($set)) {
+    return 1 if $e == $elem;
+  }
+  return 0;
+}
+
+sub in_arr {
+  my $elem = shift;
+  my $arr = shift;
+  for my $e (@{$arr}) {
+    return 1 if $e eq $elem;
+  }
+  return 0;
+}
+
+sub map_bool {
+  my $val = shift;
+
+  return 1 if $val =~ /^true$/i;
+  return 0 if $val =~ /^false$/i;
+  return $val;
+}
+
+sub version {
+  my $ret = (@_ ? shift : 1);
+  print "$NetSNMP::Cert::PROG: $NetSNMP::Cert::VERSION\n";
+  exit($ret);
+}
+
+sub GetOptsFromArray {
+  my $args = shift;
+  local *ARGV;
+  @ARGV = @$args;
+  my $ret = ::GetOptions(@_);
+  @$args = @ARGV; # GetOptions strips out the ones it handles and leaves others
+  return $ret;
+}
+sub pull_cmd {
+  my $args = shift;
+  my $cmd;
+
+  foreach (@{$args}) {
+    if (/^(gence?rt|genca|gencsr|signcsr|showcas?|showce?rts?|import)$/) {
+      $cmd = $1;
+    }
+  }
+
+  @{$args} = grep {!/^$cmd$/} @{$args};
+
+  return $cmd;
+}
+
+
+sub usage {
+  my $ret = (@_ ? shift : 1);
+  my $arg = shift;
+
+  print "Command not implmeneted yet: $arg\n" if $ret == 2;
+  print "Unknown: $arg\n" if $ret == 3;
+  print "\n   NAME:\n";
+  print "     $NetSNMP::Cert::PROG: [$NetSNMP::Cert::VERSION] - ";
+  print "Net-SNMP Certificate Management Tool\n";
+  print "\n   DESCRIPTION:\n";
+  print "     net-snmp-cert creates, signs, installs and displays X.509\n";
+  print "     certificates used in the operation of Net-SNMP/(D)TLS.\n";
+  print "\n   SYNOPSIS:\n";
+  print "     net-snmp-cert [--help|-?]\n";
+  print "     net-snmp-cert [--version|-V]\n";
+  print "     net-snmp-cert genca    [<flags>] [<dn-opts>] [--with-ca <ca>]\n";
+  print "     net-snmp-cert gencert  [<flags>] [<dn-opts>] [--with-ca <ca>]\n";
+  print "     net-snmp-cert gencsr   [<flags>] [<dn-opts>] [--from-crt <crt>]\n";
+  print "     net-snmp-cert signcsr  [<flags>] [--install] --csr <csr> --with-ca <ca>\n";
+  print "     net-snmp-cert showca   [<flags>] [<format-opts>] [<file>|<search-tag>]\n";
+  print "     net-snmp-cert showcert [<flags>] [<format-opts>] [<file>|<search-tag>]\n";
+  print "     net-snmp-cert import   [<flags>] <file|url> [<key>]\n";
+  print "\n   COMMANDS:\n";
+  print "     genca    -- generate a signed CA certificate suitable for signing other\n";
+  print "                 certificates. default: self-signed unless --with-ca <ca> supplied\n\n";
+  print "     gencert  -- generate a signed certificate suitable for identification, or\n";
+  print "                 validation. default: self-signed unless --with-ca <ca> supplied\n\n";
+  print "     gencsr   -- generate a certificate signing request. will create a new\n";
+  print "                 key and certificate unless --from-crt <crt> supplied\n\n";
+  print "     signcsr  -- sign a certificate signing request specified by --csr <csr>\n";
+  print "                 with the CA certificate specified by --with-ca <ca>\n";
+  print "     import   -- import an identity or CA certificate, optionally import <key>\n";
+  print "                 if an URL is passed, will attempt to import certificate from site\n";
+  print "     showca,\n";
+  print "     showcert -- show CA or identity certificate(s). may pass fully qualified\n";
+  print "                 file or directory name, or a search-tag which prefix matches\n";
+  print "                 installed CA or identity certificate file name(s)\n";
+  print "                 see FORMAT OPTIONS to specify output format\n\n";
+  print "\n   FLAGS:\n";
+  print "     -?, --help            -- show this text and exit\n";
+  print "     -V, --version         -- show version string and exit\n";
+  print "     -D, --debug           -- raise debug level (-D -D for higher level)\n";
+  print "     -F, --force           -- force overwrite of existing output files\n";
+  print "     -I, --nointeractive   -- non-interactive run (default interactive)\n";
+  print "     -Q, --noverbose       -- non-verbose output (default verbose)\n";
+  print "     -C, --cfgdir   <dir>  -- configuration dir (see man(5) snmp_config)\n";
+  print "     -T, --tlsdir   <dir>  -- root for cert storage (default <cfgdir>/tls)\n";
+  print "     -f, --cfgfile  <file> -- config (default <cfgdir>/net-snmp-cert.conf)\n";
+  print "     -i, --identity <id>   -- identity to use from config\n";
+  print "     -t, --tag      <tag>  -- application tag (default 'snmp')\n";
+  print "     --<cfg-param>[=<val>] -- additional config params\n";  
+  print "\n   CERTIFICATE OPTIONS (<cert-opts>):\n";
+  print "     -a, --with-ca <ca>    -- CA certificate used to sign request\n";
+  print "     -A, --ca <ca>         -- explicit output CA certificate\n";
+  print "     -r, --csr <csr>       -- certificate signing request\n";
+  print "     -x, --from-crt <crt>  -- input certificate for current operation\n";
+  print "     -X, --crt <crt>       -- explicit output certificate\n";
+  print "     -y, --install         -- install result in local repository\n";
+  print "\n   DISTINGUISHED NAME OPTIONS (<dn-opts>):\n";
+  print "     -e, --email <email>       -- email name\n";
+  print "     -h, --host <host>         -- DNS host name, or IP address\n";
+  print "     -d, --days <days>         -- number of days certificate is valid\n";
+  print "     -n, --cn <cn>             -- common name (CN)\n";
+  print "     -o, --org <org>           -- organiztion name\n";
+  print "     -u, --unit <unit>         -- organiztion unit name\n";
+  print "     -c, --country <country>   -- country code (e.g., US)\n";
+  print "     -p, --province <province> -- province name (synomynous w/ state)\n";
+  print "     -p, --state <state>       -- state name (synomynous w/ province)\n";
+  print "     -l, --locality <locality> -- locality name (e.g, town)\n";
+  print "     -s, --san <san>           -- subjectAltName, repeat for each <san>\n";
+  print "                               -- <san> value format (<FMT>:<VAL>):\n";
+  print "                               --   dirName:/usr/share/snmp/tls\n";
+  print "                               --   DNS:net-snmp.org\n";
+  print "                               --   email:admin\@net-snmp.org\n";
+  print "                               --   IP:192.168.1.1\n";
+  print "                               --   RID:1.1.3.6\n";
+  print "                               --   URI:http://net-snmp.org\n";
+  print "\n   FORMAT OPTIONS (<format-opts>): \n";
+  print "     --brief        -- minimized output (values only where applicable)\n";
+  print "     --text         -- full text description\n";
+  print "     --subject      -- subject description\n";
+  print "     --subject_hash -- hash of subject for indexing\n";
+  print "     --issuer       -- issuer description\n";
+  print "     --issuer_hash  -- hash of issuer for indexing\n";
+  print "     --fingerprint  -- SHA1 digest of DER\n";
+  print "     --serial       -- serial number\n";
+  print "     --modulus      -- modulus of the public key\n";
+  print "     --dates        -- start and end dates\n";
+  print "     --purpose      -- displays allowed uses\n";
+  print "     --C            -- C code description\n";
+  print "\n   EXAMPLES: \n";
+  print "     net-snmp-cert genca --cn ca-net-snmp.org --days 1000\n";
+  print "     net-snmp-cert genca -f .snmp/net-snmp-cert.conf -i nocadm -I\n";
+  print "     net-snmp-cert gencert -t snmpd --cn host.net-snmp.org\n";
+  print "     net-snmp-cert gencsr -t snmpapp\n";
+  print "     net-snmp-cert signcsr --csr snmpapp --with-ca ca-net-snmp.org\n";
+  print "     net-snmp-cert showcerts --subject --issuer --dates snmpapp\n";
+  print "     net-snmp-cert showcas --fingerprint ca-net-snmp.org --brief\n";
+  print "     net-snmp-cert import ca-third-party.crt\n";
+  print "     net-snmp-cert import signed-request.crt signed-request.key\n\n";
+
+  exit $ret;
+}
+
+sub set_default {
+  my $opts = shift;
+  my $config = shift;
+  my $field = shift;
+  my $val = shift;
+
+  if (not defined $opts->{$field}) {
+    my $cval = $config->inherit($field);
+    $opts->{$field} = (defined $cval ? $cval : $val);
+  }
+}
+
+sub cfg_path {
+  my $path;
+
+  $path = "/etc/snmp:/usr/share/snmp:/usr/lib/snmp:/var/lib/net-snmp";
+  return (wantarray ? split(':', $path) : $path);
+}
+
+sub find_cfgfile {
+  my $dir = shift;
+  my $file = shift;
+
+  if (defined $dir and -d $dir and 
+      defined $file and $file !~ /^[\.\/]/) {
+    return fq_rel_path("$dir/$file") if -f "$dir/$file";
+  }
+
+  if (defined $file) {
+    if (-f $file) {
+      return fq_rel_path($file);
+    } else {
+      return $file; # file is not found, complain later
+    }
+  }
+
+  my @path = cfg_path();
+  unshift(@path, $dir) if defined $dir;
+  while (@path) {
+    my $p = pop(@path);
+    next if $p eq '/var/lib/snmp';
+    if (-r "$p/$NetSNMP::Cert::CFGFILE") {
+      return fq_rel_path("$p/$NetSNMP::Cert::CFGFILE");
+    }
+  }
+
+  return $file;
+}
+
+sub find_cfgdir {
+  my $dir = shift;
+  my $file = shift;
+
+  if (defined $dir) {
+    $dir = NetSNMP::Cert::fq_rel_path($dir);
+    return $dir;
+  }
+
+  if (defined $file and -f $file) {
+    $dir = ::dirname($file);
+    return NetSNMP::Cert::fq_rel_path($dir);
+  } else {
+    my @path = cfg_path();
+    # search first for writeable tls dir
+    # for root search top down, for user bottom up
+    while (@path) {
+      $dir = ($> ? pop(@path): shift(@path));
+      next if $dir eq '/var/lib/snmp';
+      return $dir if -d "$dir/$NetSNMP::Cert::TLSDIR" and -w "$dir/$NetSNMP::Cert::TLSDIR";
+    }
+    @path = cfg_path();
+    # if no tls dir found, pick first writable config dir
+    # for root search top down, for user bottom up
+    while (@path) {
+      $dir = ($> ? pop(@path): shift(@path));
+      next if $dir eq '/var/lib/snmp';
+      return $dir if -d $dir and -w $dir;
+    }
+    my $home = $ENV{HOME} || die "Unable to determine home directory: set \$HOME\n";
+    return ($> ? "$home/.snmp" : "/usr/share/snmp"); # XXX hack - no dirs existed or were writable
+  }
+  # this should never happen
+  return undef;
+}
+
+sub find_certs {
+  my $opts = shift;
+  my $dir = shift || 'certs';
+  my $targ = shift;
+  my $suffix_regex;
+  my $cwd = ::getcwd();
+
+  if ($dir eq 'csrs') {
+    $dir = $NetSNMP::Cert::NEWCRTDIR;
+    $suffix_regex = '\.csr|\.pem';
+  } else {
+    $suffix_regex = '\.crt|\.pem';
+  }
+
+  NetSNMP::Cert::dprint("find_certs(1:$cwd):$dir:$targ:$suffix_regex\n", $opts);
+
+  if ($targ =~ /\.?\//) {
+    # see if targ could be file - calc orig. rel. path
+    my $arg = NetSNMP::Cert::fq_rel_path($targ, $opts->{'rdir'});
+    NetSNMP::Cert::dprint("find_certs(2):$dir:$targ:$arg\n", $opts);
+    # targ is a file name - use it
+    return (wantarray ? ($arg) : $arg) if -f $arg;
+    # else mark as dir if it is
+    $targ = "$arg" if -d $arg;
+  }
+
+  $targ =~ s/\/*$/\// if -d $targ;
+
+  NetSNMP::Cert::dprint("find_certs(3):$dir:$targ\n", $opts);
+
+  # find certs in targ if a dir (in tlsdir, or relative)
+  $dir = $1 if $targ =~ s/^(\S+)\/([^\/\s]*)$/$2/ and -d $1;
+
+  NetSNMP::Cert::dprint("find_certs(4):${dir}:$targ:$cwd\n", $opts);
+
+  my @certs;
+  my $glob = "$dir/$targ";
+  foreach (<$dir/*>) {
+    NetSNMP::Cert::dprint("checking($dir:$targ): $_\n", $opts);
+    next unless /^$dir\/$targ(.*$suffix_regex)?$/;
+    # return exact match if not wantarray()
+    return $_ if (not wantarray()) and /^$dir\/$targ($suffix_regex)?$/;
+    NetSNMP::Cert::dprint("pushing: $_\n", $opts);
+    push(@certs, $_);
+  }
+
+  return (wantarray ? @certs : $certs[0]);
+}
+
+sub check_output_file {
+  my $opts = shift;
+  my $file = shift;
+  my $interactive = shift;
+  my $force = shift;
+  my $continue = 1;
+
+  if (-w $file) {
+    if ($interactive and not $force) {
+      print "Output file ($file) exists; will be overwritten\nContinue [y/N]: ";
+      $continue = <STDIN>; chomp $continue;
+      $continue = 0 if $continue =~ /^\s*$|^no?\s*$/i;
+    } else {
+      if ($force) {
+	NetSNMP::Cert::vprint("Output file ($file) exists; overwriting...\n", $opts);
+      } else {
+	NetSNMP::Cert::vprint("Output file ($file) exists; exiting...\n", $opts);
+	$continue = 0;
+      }
+    }
+  } elsif (-e $file) {
+    NetSNMP::Cert::vprint("Output file ($file) not writable; exiting...\n", $opts);
+    $continue = 0;
+  }
+  exit(1) unless $continue;
+}
+
+sub is_server {
+  my $tag = shift;
+  return 1 if $tag eq 'snmpd' or $tag eq 'snmptrapd';
+  return 0;
+}
+
+sub is_ca_cert {
+  my $crt = shift;
+  my $output = `openssl x509 -in '$crt' -text -noout`;
+  return ($output =~ /^\s*CA:TRUE\s*$/m ? 1 : 0);
+}
+
+sub x509_format {
+  my $opts = shift;
+  my $fmt;
+
+  foreach my $f (@NetSNMP::Cert::X509FMTS) {
+    $fmt .= " -$f" if defined $opts->{$f};
+  }
+
+  return $fmt;
+}
+
+sub make_openssl_conf {
+  my $file = shift;
+  return if -r $file;
+
+  open(F, ">$file") or die("could not open $file");
+
+  print F <<'END';
+#
+# Net-SNMP (D)TLS OpenSSL configuration file.
+#
+
+rdir            = .
+dir		= $ENV::DIR
+RANDFILE	= $rdir/.rand
+MD		= sha1
+KSIZE		= 2048
+CN		= net-snmp.org
+EMAIL		= admin@net-snmp.org
+COUNTRY	        = US
+STATE		= CA
+LOCALITY	= Davis
+ORG		= Net-SNMP
+ORG_UNIT	= Development
+SAN             = email:copy
+DAYS            = 365
+CRLDAYS         = 30
+
+default_days	= $ENV::DAYS		# how long to certify for
+default_crl_days= $ENV::CRLDAYS		# how long before next CRL
+default_md	= $ENV::MD		# which md to use.
+
+database	= $dir/.index		# database index file.
+serial		= $dir/.serial 		# The current serial number
+certs		= $rdir/certs		# identity certs
+new_certs_dir	= $dir/newcerts	# default place for new certs.
+ca_certs_dir	= $rdir/ca-certs	# default place for new certs.
+key_dir		= $rdir/private
+
+crl_dir		= $dir/crl		# crl's
+crlnumber	= $dir/.crlnumber	# the current crl number
+					# must be commented out to leave V1 CRL
+crl		= $crl_dir/crl.pem	# The current CRL
+
+preserve	= no			# keep passed DN ordering
+unique_subject	= yes			# Set to 'no' to allow creation of
+					# certificates with same subject.
+# Extra OBJECT IDENTIFIER info:
+oid_section		= new_oids
+
+[ new_oids ]
+
+# Add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca	= CA_default		# The default ca section
+
+####################################################################
+[ CA_default ]
+# certificate	= $ca_certs_dir/$ENV::TAG.crt 	# CA certificate so sign with
+name_opt 	= ca_default		# Subject Name options
+cert_opt 	= ca_default		# Certificate field options
+policy		= policy_match
+copy_extensions = copy                  # copy v3 extensions (subjectAltName)
+subjectAltName  = copy
+
+# For the CA policy
+[ policy_match ]
+countryName		= match
+stateOrProvinceName	= match
+organizationName	= match
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName		= optional
+stateOrProvinceName	= optional
+localityName		= optional
+organizationName	= optional
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
+
+####################################################################
+[ req ]
+default_bits		= $ENV::KSIZE
+default_md		= $ENV::MD
+distinguished_name	= req_distinguished_name
+string_mask 		= MASK:0x2002
+req_extensions          = v3_req
+x509_extensions         = v3_user_create
+
+[ req_distinguished_name ]
+countryName			= Country Name (2 letter code)
+countryName_default		= $ENV::COUNTRY
+countryName_min			= 2
+countryName_max			= 2
+
+stateOrProvinceName		= State or Province Name (full name)
+stateOrProvinceName_default	= $ENV::STATE
+
+localityName			= Locality Name (eg, city)
+localityName_default		= $ENV::LOCALITY
+
+0.organizationName		= Organization Name (eg, company)
+0.organizationName_default	= $ENV::ORG
+
+organizationalUnitName		= Organizational Unit Name (eg, section)
+organizationalUnitName_default	= $ENV::ORG_UNIT
+
+commonName			= Common Name (eg, your name or your server\'s hostname)
+commonName_max			= 64
+commonName_default              = $ENV::CN
+
+emailAddress			= Email Address
+emailAddress_max		= 64
+emailAddress_default            = $ENV::EMAIL
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+basicConstraints = CA:FALSE
+
+# Import the email address and/or specified SANs.
+%[subjectAltName = $ENV::SAN]
+
+[ v3_user_create ]
+
+# Extensions to add to a certificate request
+basicConstraints = CA:FALSE
+#keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# PKIX recommendation.
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+
+# Import the email address and/or specified SANs.
+%[subjectAltName = $ENV::SAN]
+
+[ v3_ca_create ]
+# Extensions to add to a CA certificate
+basicConstraints = CA:TRUE
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate (net-snmp)"
+
+# PKIX recommendation.
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+
+# Import the email address and/or specified SANs.
+%[subjectAltName = $ENV::SAN]
+
+[ v3_ca_sign ]
+# Extensions to add when 'ca' signs a request.
+basicConstraints = CA:FALSE
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate (net-snmp)"
+
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+
+[ v3_ca_sign_ca ]
+# Extensions to add when 'ca' signs a ca request.
+basicConstraints = CA:TRUE
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate (net-snmp)"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+
+END
+
+}
+
+
+package NetSNMP::Cert::Obj;
+
+sub new {
+  my $class = shift;
+  my $cfield = shift;
+  my $parent = shift;
+  my $ind = shift;
+  my $this = {};
+  bless($this, $class);
+
+  # initialize hash of keys which are dynamically created or internal
+  $this->{'AUTOKEYS'}{'AUTOKEYS'}++;
+
+  # store a reference to ourselves so our children can find us
+  $this->autoSet('CFIELD', $cfield);
+  $this->autoSet($cfield , $this);
+  $this->autoSet('CHILDREN', []);
+  $this->autoSet('INDEX', $ind) if defined $ind;
+
+  if (defined $parent) {
+    # cache 'APP' in all objs for easy reference
+    $this->autoSet('APP', $parent->inherit('APP'));
+    my $app = $this->{'APP'};
+
+    die("net-snmp-cert: error: no NetSNMP::Cert::App context provided")
+      if not defined $app or not ref $app eq 'NetSNMP::Cert::App';
+    # save children for list traversal
+    push(@{$parent->{'CHILDREN'}}, $this) unless $parent eq $this;
+  } else {
+    # we are the top of the list
+    $parent = $this;
+  }
+  $this->autoSet('PARENT' , $parent);
+
+  return $this;
+}
+
+sub autoSet {
+  my $this = shift;
+  my $key = shift;
+  if (@_) {
+    my $val = shift;
+    $this->{'AUTOKEYS'}{$key}++;
+    $this->{$key} = $val;
+  }
+  return exists $this->{'AUTOKEYS'}{$key};
+}
+
+sub inherit {
+  my $this = shift;
+  my $field = shift;
+  my $id;
+
+  # cmd opts override config settings
+  if (exists $this->{'APP'} and exists $this->{'APP'}{'OPTS'}) {
+    my $opts = $this->{'APP'}{'OPTS'};
+    $id = $opts->{'identity'};
+    return $opts->{$field} if defined $opts->{$field};
+  }
+
+  if (defined $id and exists $this->{'identity'} and
+      exists $this->{'identity'}{$id} and
+      exists $this->{'identity'}{$id}{$field}) {
+    return $this->{'identity'}{$id}{$field};
+  }
+
+  # return our field if we have it
+  return $this->{$field} if defined $this->{$field};
+
+  # terminate recursion at top and return undef if not found
+  return undef if not defined $this->{'PARENT'} or $this->{'PARENT'} eq $this;
+
+  # recurse to parent
+  $this->{'PARENT'}->inherit($field);
+}
+
+sub resolve {
+  my $this = shift;
+  my $opts = $this->inherit('OPTS');
+  my $val = shift;
+
+  NetSNMP::Cert::dprint("resolving: $val\n", $opts);
+
+  $val =~ s/(\$(\w+))/$this->inherit($2) or die("unresolved reference in config: $1")/ge;
+  $val =~ s/(\&\{(.*?)\})/$2/gee;
+
+  NetSNMP::Cert::dprint("resolved: $val\n", $opts);
+
+  return $val
+}
+
+sub delete {
+  my $this = shift;
+  my $opts = $this->inherit('OPTS');
+
+  NetSNMP::Cert::dprint("Obj::delete: ($this) [$this->{CFIELD}]\n", $opts);
+
+  my $parent = $this->{'PARENT'};
+  my @children = @{$this->{'CHILDREN'}};
+
+  foreach my $child (@children) {
+    $child->delete();
+  }
+
+  NetSNMP::Cert::dwarn("Obj: children not freed\n", $opts) if @{$this->{'CHILDREN'}};
+
+  # delete all our self-references
+  delete($this->{$this->{'CFIELD'}}) if exists $this->{'CFIELD'};
+  delete($this->{'PARENT'});
+  delete($this->{'CHILDREN'});
+
+  $parent->disown($this) if defined $parent and ref($parent) =~ /NetSNMP::Cert/;
+}
+
+sub disown {
+  my $this = shift;
+  my $thechild = shift;
+  my $opts = $this->inherit('OPTS');
+  my $ind = 0;
+
+  NetSNMP::Cert::dprint("Obj::disown: ($this) [$this->{CFIELD}] disowning ($thechild) [$thechild->{CFIELD}]\n", $opts);
+
+  foreach my $child (@{$this->{'CHILDREN'}}) {
+    last if $child eq $thechild;
+    $ind++;
+  }
+  if ($ind < @{$this->{'CHILDREN'}}) {
+    splice(@{$this->{'CHILDREN'}}, $ind, 1);
+  } else {
+    NetSNMP::Cert::dwarn("Child ($thechild) not found in object ($this)\n", $opts);
+  }
+}
+
+sub disabled {
+  my $this = shift;
+  return (exists $this->{'disable'} ? $this->{'disable'} : 0);
+}
+
+my %cfg = (
+	   'name' => 1,
+	   'type' => 2,
+	   'id' => 6,
+	  );
+
+sub cfgsort {
+  my $self = shift;
+  my $a = shift;
+  my $b = shift;
+  return -1 if exists $cfg{$a} and not exists $cfg{$b};
+  return 1 if exists $cfg{$b} and not exists $cfg{$a};
+  return $cfg{$a} <=> $cfg{$b} if exists $cfg{$a} and exists $cfg{$b};
+  return -1 if !ref($self->{$a}) and ref($self->{$b});
+  return 1 if !ref($self->{$b}) and ref($self->{$a});
+  return -1 if ref($self->{$a}) =~ /NetSNMP::Cert/ and ref($self->{$b}) !~ /NetSNMP::Cert/;
+  return 1 if ref($self->{$b}) =~ /NetSNMP::Cert/ and ref($self->{$a}) !~ /NetSNMP::Cert/;
+  return 0;
+}
+
+sub dump {
+  my $self = shift;
+  my $indent = shift;
+  my $self_str = $self->{'CFIELD'};
+  my @data_keys =  grep {!$self->autoSet($_)} keys %$self;
+  my @lines;
+
+  push(@lines, "\n" . ' ' x $indent . "$self_str = {\n") if defined $indent;
+
+  {
+    my $indent = (defined $indent ? $indent + 3 : 0);
+    foreach my $key (sort {cfgsort($self,$NetSNMP::Cert::Obj::a,
+				   $NetSNMP::Cert::Obj::b)} (sort @data_keys)) {
+      if (ref($self->{$key}) =~ /NetSNMP::Cert/) {
+	push(@lines, $self->{$key}->dump($indent));
+      } elsif (ref($self->{$key}) =~ /ARRAY/) {
+	push(@lines, "\n") if ref(${$self->{$key}}[0]) !~ /NetSNMP::Cert/;
+	foreach my $elem (@{$self->{$key}}) {
+	  if (ref($elem) =~ /NetSNMP::Cert/) {
+	    push(@lines, $elem->dump($indent));
+	  } else {
+	    push(@lines, ' ' x $indent . "$key = $elem\n");
+	  }
+	}
+      } else {
+	my $str = $key . (defined $self->{$key} ? " = $self->{$key}\n" : "\n");
+	push(@lines, ' ' x $indent . $str);
+      }
+    }
+  }
+
+  push(@lines, ' ' x $indent . "}; # end $self_str\n") if defined $indent;
+  return @lines;
+}
+
+sub DESTROY {
+  my $this = shift;
+
+  print("Obj::DESTROY $this [", ref $this, "]\n") if $NetSNMP::Cert::DEBUG >= 3;
+}
+
+package NetSNMP::Cert::App;
+use vars qw(@ISA);
+
+@ISA = qw(NetSNMP::Cert::Obj);
+
+sub new {
+  my $class = shift;
+
+  # the app is god, it is its own parent
+  my $this = $class->SUPER::new('APP');
+
+  bless($this, $class);
+
+  # verify required tools or die
+  $this->checkReqs();
+
+  # internal intitialization
+  $this->initOpts();
+
+  # make a new empty config and init (not parsed)
+  $this->{'config'} = new NetSNMP::Cert::Config($this);
+
+  return $this;
+}
+
+sub checkReqs {
+  my $app = shift;
+
+  die("$NetSNMP::Cert::OPENSSL does not exist or is not executable")
+    if system("$NetSNMP::Cert::OPENSSL version > /dev/null 2>&1");
+
+  my $ossl_ver = `$NetSNMP::Cert::OPENSSL version`; chomp $ossl_ver;
+  $ossl_ver =~ s/^OpenSSL\s+([\d\.]+).*$/$1/;
+  my $ossl_min_ver = $NetSNMP::Cert::OPENSSL_MIN_VER;
+
+  die("$NetSNMP::Cert::OPENSSL (v$ossl_ver): must be $ossl_min_ver or later")
+    if ($ossl_ver cmp $ossl_min_ver) < 0;
+
+#  die("$NetSNMP::Cert::CFGTOOL not found: please install")
+#    if system("$NetSNMP::Cert::CFGTOOL > /dev/null 2>&1");
+}
+
+sub initOpts {
+  my $app = shift;
+  my $opts = {};
+  $app->autoSet('OPTS', $opts);
+
+  # Define directories we need.
+  $opts->{'bindir'} = NetSNMP::Cert::find_bin_dir();
+
+  $opts->{'out'} = "> $NetSNMP::Cert::OUTLOG";
+  $opts->{'err'} = "2> $NetSNMP::Cert::ERRLOG";
+
+  # set up paths for app install and runtime env
+  $ENV{PATH} = "/sbin:/usr/sbin:$ENV{PATH}";
+  $ENV{PATH} = "$opts->{'bindir'}:$ENV{PATH}";
+
+  # default all privs to -rw-------
+  umask(077);
+}
+
+sub init {
+  my $app = shift;
+  my $opts = $app->{'OPTS'};
+  my $config = $app->{'config'};
+  my @args = @_;   # pass external args (typically ARGV)
+
+  # parse command line
+  $app->parseArgs(@args);
+
+  # lazy config parsing postponed until here, will not reparse
+  $config->parse();
+
+  # set defaults here if not already set in cmdline or config
+  # guided interactive mode by default
+  NetSNMP::Cert::set_default($opts, $config, 'interactive', 1);
+  # verbose output
+  NetSNMP::Cert::set_default($opts, $config, 'verbose', 1);
+
+  # find tlsdir/subdirs or make it
+  $opts->{'tlsdir'} = $app->createTlsDir();
+}
+
+sub parseArgs {
+  my $app = shift;
+  my $opts = $app->{'OPTS'};
+  my @args = @_;
+
+  NetSNMP::Cert::GetOptsFromArray(\@args, $opts, 'help|?', 'version|V',
+		      'interactive!', 'I', 'verbose!', 'Q', 'force|F',
+		      'cfgfile|f=s', 'cfgdir|C=s', 'tlsdir|tlsDir|T=s',
+		      'tag|t=s', 'identity|i=s', 'debug|D+',);
+
+  NetSNMP::Cert::version(0) if $opts->{'version'};
+
+  NetSNMP::Cert::usage(0) if $opts->{'help'};
+
+  # pull out the cmd - getOpts should leave it untouched for us
+  $opts->{'cmd'} = NetSNMP::Cert::pull_cmd(\@args);
+
+  # save extra args for command specific processing
+  $opts->{'cmdargs'} = [@args];
+
+  # Check debug option first
+  $NetSNMP::Cert::DEBUG = $opts->{'debug'};
+  $opts->{'err'} = $opts->{'out'} = "" if $opts->{'debug'};
+  $opts->{'interactive'} = not $opts->{'I'} if defined $opts->{'I'};
+  $opts->{'verbose'} = not $opts->{'Q'} if defined $opts->{'Q'};
+
+  # search for cfgdir and cfgfile based on opts and confpath
+  $opts->{'cfgdir'} = NetSNMP::Cert::find_cfgdir($opts->{'cfgdir'},
+						 $opts->{'cfgfile'});
+  $opts->{'cfgfile'} = NetSNMP::Cert::find_cfgfile($opts->{'cfgdir'},
+						   $opts->{'cfgfile'});
+}
+
+sub createTlsDir {
+  my $app = shift;
+  my $opts = $app->{'OPTS'};
+  my $config = $app->{'config'};
+  my $dir;
+  my $file;
+  my $cmd;
+
+  $dir = $config->inherit('tlsDir');
+
+  if (not defined $dir) {
+    my $cfgdir = $opts->{'cfgdir'};
+    die("undefined cfgdir: unable to creat tlsdir: exiting...\n") unless defined $cfgdir;
+    $dir = "$cfgdir/$NetSNMP::Cert::TLSDIR";
+  }
+
+  NetSNMP::Cert::dprint("tlsDir is: $dir\n", $opts);
+  $dir = NetSNMP::Cert::fq_rel_path($dir);
+  NetSNMP::Cert::dprint("tlsDir is: $dir\n", $opts);
+
+  NetSNMP::Cert::dprint("tlsDir not found, creating\n", $opts) unless -d $dir;
+  NetSNMP::Cert::make_dirs($opts, $dir, 0700, @NetSNMP::Cert::TLSDIRS);
+
+  my $ssl_cfg_in = NetSNMP::Cert::fq_rel_path($NetSNMP::Cert::SSLCFGIN,$dir);
+
+  # Existing openssl.conf tmpl will be preserved
+  if (-f $ssl_cfg_in) {
+    NetSNMP::Cert::dwarn("OpenSSL template exists ($ssl_cfg_in): preserving...", $opts);
+  }
+
+  if (not -f $ssl_cfg_in) {
+    NetSNMP::Cert::dprint("make_openssl_conf($ssl_cfg_in)", $opts);
+    NetSNMP::Cert::make_openssl_conf($ssl_cfg_in);
+    chmod(0600, $ssl_cfg_in);
+  }
+
+  NetSNMP::Cert::dprint("createTlsDir: done\n", $opts);
+  return $dir;
+}
+
+my @interactive_ops = (['gencert', "Generate a signed certificate"],
+	   ['genca', "Generate a CA certificate"],
+	   ['gencsr', "Generate a Certificate Signing Request"],
+	   ['signcsr', "Sign a Certificate Signing Request"],
+	  );
+
+sub run {
+  my $app = shift;
+  my $opts = $app->{'OPTS'};
+  my $cmd = $opts->{'cmd'};
+
+  # must have a command in non-Interactive mode
+  NetSNMP::Cert::usage(3) if !$opts->{'interactive'} and !$opts->{'cmd'};
+
+  #  change dir tls dir - the cwd for all commands - save cwd first
+  $opts->{'rdir'} = ::getcwd();
+  chdir($opts->{'tlsdir'}) or die("could'nt change directory: $opts->{tlsdir}");
+  # display context
+  NetSNMP::Cert::dprint("PATH: $ENV{PATH}\n\n", $opts);
+  NetSNMP::Cert::dprint("config file: $opts->{cfgfile}\n", $opts);
+  NetSNMP::Cert::dprint("config dir:  $opts->{cfgdir}\n", $opts);
+  NetSNMP::Cert::dprint("tls dir:     $opts->{tlsdir}\n", $opts);
+
+  my $cmdstr = join(' ', $cmd, @{$opts->{'cmdargs'}});
+  NetSNMP::Cert::dprint("command:     $cmdstr\n", $opts);
+
+  NetSNMP::Cert::GetOptsFromArray(\@{$opts->{'cmdargs'}}, $opts,
+				  'with-ca|a=s', 'ca|A=s','csr|r=s',
+				  'from-crt|x=s', 'crt|X=s', 'days|d=s',
+				  'cn|n=s', 'email|e=s', 'host|h=s',
+				  'san|s=s@', 'org|o=s', 'unit|u=s',
+				  'country|c=s', 'state|province|p=s',
+				  'locality|l=s', 'brief|b',
+				  @NetSNMP::Cert::X509FMTS);
+
+  # process extra args; --<cfg-name>[=<val>]
+  $app->processExtraArgs();
+
+  # If in interactive mode - fill missing info by interviewing user
+  if (not defined $cmd or grep {/$cmd/} map {$_->[0]} @interactive_ops) {
+    $app->userInput() if $opts->{interactive};
+    $cmd = $opts->{'cmd'}; # may have changed
+  }
+
+  # resolve input args to filenames
+  $app->resolveCrtArgs();
+
+  # use env. or merge template for OpenSSL config
+  $NetSNMP::Cert::SSLCFG ||= $app->opensslCfg();
+
+  if ($cmd =~ /^genca$/) {
+    NetSNMP::Cert::usage(1) if defined $opts->{'from-crt'} or defined $opts->{'csr'};
+    $app->genCa($opts->{'with-ca'});
+  } elsif ($cmd =~ /^gence?rt$/) {
+    NetSNMP::Cert::usage(1) if defined $opts->{'from-crt'} or defined $opts->{'csr'};
+    $app->genCert($opts->{'with-ca'});
+  } elsif ($cmd =~ /^gencsr$/) {
+    NetSNMP::Cert::usage(1) if defined $opts->{'with-ca'} or defined $opts->{'crt'};
+    $app->genCsr();
+  } elsif ($cmd =~ /^signcsr$/) {
+    NetSNMP::Cert::usage(1) unless defined $opts->{'with-ca'} and defined $opts->{'csr'};
+    $app->signCsr();
+  } elsif ($cmd =~ /^show(ce?rts?)?$/) {
+    $app->show('certs');
+  } elsif ($cmd =~ /^showcas?$/) {
+    $app->show('ca-certs');
+  } elsif ($cmd =~ /^import$/) {
+    $app->import();
+  } else {
+    NetSNMP::Cert::usage();
+  }
+}
+
+sub processExtraArgs {
+  my $app = shift;
+  my $opts = $app->{'OPTS'};
+  my @args;
+
+  NetSNMP::Cert::dprint("processing extra args...\n", $opts);
+
+  while (@{$opts->{'cmdargs'}}) {
+    my $arg = shift(@{$opts->{'cmdargs'}});
+    NetSNMP::Cert::dprint("found: arg --> $arg\n", $opts);
+    if ($arg =~ /^-+(\w+)(?:=(.*?))?\s*$/) {
+      NetSNMP::Cert::dprint("found: arg($1) val($2)\n", $opts);
+      if (exists $opts->{$1}) {
+   	NetSNMP::Cert::vwarn("option ($1) already set: overwriting\n", $opts);
+      } 
+      $opts->{$1} = (defined $2 ? $2 : 1);
+    } else {
+      push(@args, $arg);
+    }
+  }
+  @{$opts->{'cmdargs'}} = @args;
+}
+
+sub resolveCrtArgs {
+  my $app = shift;
+  my $opts = $app->{'OPTS'};
+
+  # find ca, crt, csr args if present and return fully qualified path
+  if (defined $opts->{'with-ca'}) {
+    my $ca;
+    $ca = NetSNMP::Cert::find_certs($opts, 'ca-certs', $opts->{'with-ca'});
+    die("unable to locate CA certificate ($opts->{'with-ca'})\n") unless -e $ca;
+    die("unable read CA certificate ($opts->{'with-ca'})\n") unless -r $ca;
+    $opts->{'with-ca'} = $ca;
+  }
+
+  if (defined $opts->{'from-crt'}) {
+    my $crt;
+    $crt = NetSNMP::Cert::find_certs($opts, 'certs', $opts->{'from-crt'});
+    die("unable to locate certificate ($opts->{'from-crt'})\n") unless -e $crt;
+    die("unable read certificate ($opts->{'from-crt'})\n") unless -r $crt;
+    $opts->{'from-crt'} = $crt;
+  }
+
+  if (defined $opts->{'csr'}) {
+    my $csr;
+    $csr = NetSNMP::Cert::find_certs($opts, 'csrs', $opts->{'csr'});
+    die("unable to locate CSR certificate ($opts->{csr})\n") unless -e $csr;
+    die("unable read CSR certificate ($opts->{csr})\n") unless -r $csr;
+    $opts->{'csr'} = $csr;
+  }
+}
+
+sub opensslCfg {
+  my $app = shift;
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $san = $config->inherit('san') || $config->inherit('subjectAltName');
+  my $ssl_cfg_in = NetSNMP::Cert::fq_rel_path($NetSNMP::Cert::SSLCFGIN);
+  my $ssl_cfg_out = NetSNMP::Cert::fq_rel_path($NetSNMP::Cert::SSLCFGOUT);
+
+  if (not -f $ssl_cfg_in) {
+    NetSNMP::Cert::vwarn("OpenSSL template not found: $ssl_cfg_in\n", $opts);
+    die("no OpenSSL template");
+  }
+
+  open(IN, $ssl_cfg_in) or die("unable to open OpenSSL template: $ssl_cfg_in\n");
+  open(OUT, ">$ssl_cfg_out") or die("unable to open OpenSSL config: $ssl_cfg_out\n");
+
+  print OUT "#######################################################\n";
+  print OUT "#####    Warning: Do Not Edit - Generated File    #####\n";
+  print OUT "#######################################################\n";
+  while (<IN>) {
+    if ($san) {
+      s/\%\[([^\]]*?)\]/$1/;
+    } else {
+      s/\%\[([^\]]*?)\]//;
+    }
+    print OUT $_;
+  }
+  close(IN);
+  close(OUT);
+
+  return $ssl_cfg_out;
+}
+
+sub opensslEnv {
+  my $app = shift;
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+
+  my $cn = shift;
+  my $days = shift;
+  my $dir = shift || ".";
+  # XXX - need to handle config'd identity here
+  my $name = $config->inherit("name");
+  my $host = $config->inherit("host");
+  my $email = $config->inherit("email");
+  my $country = $config->inherit("country");
+  my $state = $config->inherit("state");
+  my $locality = $config->inherit("locality");
+  my $org = $config->inherit("org");
+  my $org_unit = $config->inherit("unit") || $config->inherit("orgUnit");
+  my $san;
+  my $san_arr_ref;
+  my $md = $config->inherit("msgDigest");
+  my $ksize = $config->inherit("keySize");
+
+  my $env;
+
+  $env .= " KSIZE=$ksize" if defined $ksize;
+  $env .= " DAYS=$days" if defined $days;
+  $env .= " MD=$md" if defined $md;
+
+  $env .= " DIR='$dir'" if defined $dir;
+
+  $env .= " EMAIL=$email" if defined $email;
+  $env .= " CN='$cn'" if defined $cn;
+  $env .= " ORG='$org'" if defined $org;
+  $env .= " ORG_UNIT='$org_unit'" if defined $org_unit;
+  $env .= " COUNTRY=$country" if defined $country;
+  $env .= " STATE=$state" if defined $state;
+  $env .= " LOCALITY=$locality" if defined $locality;
+
+  $san_arr_ref = $config->inherit('subjectAltName');
+  $san = join('\,\ ', @{$san_arr_ref}) if ref $san_arr_ref;
+  $san_arr_ref = $config->inherit('san');
+  $san .= join('\,\ ', @{$san_arr_ref}) if ref $san_arr_ref;
+  $san =~ s/EMAIL:/email:/g;
+  $env .= " SAN=$san" if defined $san;
+
+  NetSNMP::Cert::dprint("opensslEnv: $env\n", $opts);
+
+  return $env;
+}
+our @san_prefix = (['dirName:', "e.g., dirName:/usr/share/snmp/tls"],
+		   ['DNS:', "e.g., DNS:test.net-snmp.org)"],
+		   ['email:', "e.g., email:admin\@net-snmp.org"],
+		   ['IP:', "e.g., IP:192.168.1.1"],
+		   ['RID:', "e.g., RID:1.1.3.6.20"],
+		   ['URI:', "e.g., URI:http://www.net-snmp.org"]);
+
+sub userInputDN {
+  my $app = shift;
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $prompt;
+  my $ret;
+  my $host = $config->inherit("host") || ::hostname();
+  my $email = $config->inherit('email') || getlogin() . "\@$host";
+
+ # get EMAIL
+  $prompt = "Enter Email";
+  $ret = $email;
+  $prompt .= (defined $ret ? " [$ret]: " : ": ");
+  $ret = NetSNMP::Term::Complete($prompt, $ret,
+				 "\tEmail Address - (e.g., <name>@<domain>)");
+  $email = $opts->{'email'} = $ret if defined $ret;
+  # get CN
+  $prompt = "Enter Common Name";
+  $ret = ($opts->{'cmd'} eq 'genca' ? "ca-$host" : $email);
+  $ret = $config->inherit('cn') || $config->inherit('commonName') || $ret;
+  $prompt .= (defined $ret ? " [$ret]: " : ": ");
+  $ret = NetSNMP::Term::Complete($prompt, $ret,
+				 "\tCommon Name - (e.g., net-snmp.org)");
+  $opts->{'cn'} = $ret if defined $ret;
+  # get ORG
+  $prompt = "Enter Organization";
+  $ret = $config->inherit('org') || 'Net-SNMP';
+  $prompt .= (defined $ret ? " [$ret]: " : ": ");
+  $ret = NetSNMP::Term::Complete($prompt, $ret,
+				 "\tOrganization - (e.g., Net-SNMP)");
+  $opts->{'org'} = $ret if defined $ret;
+  # get ORG_UNIT
+  $prompt = "Enter Organizational Unit";
+  $ret = $config->inherit('unit') || 'Development';
+  $prompt .= (defined $ret ? " [$ret]: " : ": ");
+  $ret = NetSNMP::Term::Complete($prompt, $ret,
+				 "\tOrganizational Unit - (e.g., Development)");
+  $opts->{'unit'} = $ret if defined $ret;
+  # get COUNTRY
+  $prompt = "Enter Country Code";
+  $ret = $config->inherit('country') || 'US';
+  $prompt .= (defined $ret ? " [$ret]: " : ": ");
+  $ret = NetSNMP::Term::Complete($prompt, $ret,
+				 "Country Code, 2 letters (<tab> for options)",
+				 $NetSNMP::Cert::MATCH, \@CC);
+  $opts->{'country'} = $ret if defined $ret;
+  # get STATE(Province)
+  $prompt = "Enter State or Province";
+  $ret = $config->inherit('state') || 'CA';
+  $prompt .= (defined $ret ? " [$ret]: " : ": ");
+  $ret = NetSNMP::Term::Complete($prompt, $ret,
+				 "\tState or Province - (e.g., CA)");
+  $opts->{'state'} = $ret if defined $ret;
+  # get LOCALITY
+  $prompt = "Enter Locality";
+  $ret = $config->inherit('locality') || 'Davis';
+  $prompt .= (defined $ret ? " [$ret]: " : ": ");
+  $ret = NetSNMP::Term::Complete($prompt, $ret, "\tLocality - (e.g., Davis)");
+  $opts->{'locality'} = $ret if defined $ret;
+  # get SAN (loop)
+  if (!$config->{'brief'}) {
+      print "Enter Subject Alt Names.  Examples:\n";
+      foreach my $pair (@san_prefix) {
+	  printf("\t%-10.10s %s\n", $pair->[0], $pair->[1]);
+      }
+  }
+  do {
+    $ret = 'done';
+    $prompt = "Enter Subject Alt Name (enter 'done' when finished) [$ret]: ";
+    $ret = NetSNMP::Term::Complete ($prompt, $ret,
+			    "\tSubject Alt Name - (<type>:<val>)",
+			     $NetSNMP::Cert::PREMATCH, \@san_prefix);
+    push(@{$opts->{'san'}}, $ret) if defined $ret and $ret ne 'done';
+  } while (defined $ret and $ret ne 'done');
+}
+
+our @snmp_apps = (['snmp', 'Generic Certificate'],
+		  ['snmpapp','Client Certificate'],
+		  ['snmpd','Agent Certificate'],
+		  ['snmptrapd','Trap-agent Certificate']);
+
+sub userInputTag {
+  my $app = shift;
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $ret;
+  my $prompt;
+
+  print "Application Tag:\n\tused to name the certificate and dictate its filename\n";
+  print "\tIt may also associate it with a particular application (eg \"snmpd\")\n";
+  print "\tif 'none', a name will be generated from other parameters\n";
+  print "\tenter <tab><tab> for typical options, or enter new name\n";
+  $prompt = "Enter Application Tag";
+  $ret = $config->inherit('tag') || 'none';
+  $prompt .= (defined $ret ? " [$ret]: " : ": ");
+  $ret = NetSNMP::Term::Complete($prompt, $ret,
+				 "Application Tag assocaiated with certificate",
+				 (not $NetSNMP::Cert::MATCH), \@snmp_apps);
+  $opts->{'tag'} = $ret if defined $ret and $ret ne 'none';
+}
+
+sub userInput {
+  my $app = shift;
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $prompt;
+  my $ret;
+
+  print "Choose an operation:\n";
+  foreach my $op (@interactive_ops) {
+    print "\t$op->[0]\t-  $op->[1]\n";
+  }
+
+  $prompt = "Operation";
+  $ret = $config->inherit('cmd') || $interactive_ops[0][0];
+  $prompt .= (defined $ret ? " [$ret]: " : ": ");
+  $ret = NetSNMP::Term::Complete($prompt, $ret,
+				 "Certifciate Operation to perform",
+				 $NetSNMP::Cert::MATCH, \@interactive_ops);
+
+  $opts->{'cmd'} = $ret;
+
+  if ($ret =~ /^gencert$/) {
+    # get tag
+    $app->userInputTag();
+    # get DN
+    $app->userInputDN();
+    # self-signed/CA-signed(ca-cert)
+  } elsif ($ret =~ /^genca$/) {
+    # get DN
+    $app->userInputDN();
+  } elsif ($ret =~ /^gencsr$/) {
+    # get tag
+    $app->userInputTag();
+    # get DN
+    $app->userInputDN();
+  } elsif ($ret =~ /^signcsr$/) {
+    # get csr
+    $prompt = "Choose Certificate Signing Request";
+    $ret = $config->inherit('csr');
+    $prompt .= (defined $ret ? " [$ret]: " : ": ");
+    $ret = NetSNMP::Term::Complete($prompt, $ret);
+    $opts->{'csr'} = $ret if defined $ret;
+    # get ca
+    $prompt = "Choose CA Certificate";
+    $ret = $config->inherit('with-ca');
+    $prompt .= (defined $ret ? " [$ret]: " : ": ");
+    $ret = NetSNMP::Term::Complete($prompt, $ret);
+    $opts->{'with-ca'} = $ret if defined $ret;
+  } else {
+    NetSNMP::Cert::vwarn("aborting operation: exiting...\n", $opts);
+    exit(1);
+  }
+}
+
+sub createCaDir {
+  my $app = shift;
+  my $dir = shift;
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $file;
+  my $cmd;
+
+  NetSNMP::Cert::make_dirs($opts, $dir, 0700,'newcerts','private');
+
+  $file = "$dir/$NetSNMP::Cert::SERIAL";
+  if (not -f $file) {
+    $cmd = "echo '01' > '$file'";
+    NetSNMP::Cert::dprint("$cmd\n", $opts);
+    NetSNMP::Cert::usystem($cmd, $opts);
+    chmod(0600, $file);
+  }
+
+  $file = "$dir/$NetSNMP::Cert::INDEX";
+  if (not -f $file) {
+    $cmd = "touch '$file'";
+    NetSNMP::Cert::dprint("$cmd\n", $opts);
+    NetSNMP::Cert::usystem($cmd, $opts);
+    chmod(0600, $file);
+  }
+}
+
+sub genCa {
+  my $app = shift;
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $host = $config->inherit('host') || ::hostname();
+  my $days = $config->inherit('days') || $config->inherit('caDays') || 
+             $NetSNMP::Cert::DEFCADAYS;
+  my $cn = $config->inherit('cn') || $config->inherit('commonName') ||
+           "ca-$host";
+  my $ca = $config->inherit('with-ca');
+  my $tag = $config->inherit('tag') || $cn;
+
+  # create CA dir
+  my $dir = ".ca/$tag";
+  $app->createCaDir($dir);
+
+  my $outCrt = "$NetSNMP::Cert::CADIR/$tag.crt";
+  my $outKey = "$NetSNMP::Cert::PRIVDIR/$tag.key";
+
+  # set command env
+  my $env = $app->opensslEnv($cn, $days);
+
+  NetSNMP::Cert::check_output_file($opts, $outCrt,
+				   $config->inherit('interactive'),
+				   $config->inherit('force'));
+  NetSNMP::Cert::check_output_file($opts, $outKey,
+				   $config->inherit('interactive'),
+				   $config->inherit('force'));
+
+  my $cmd = "$env openssl req -extensions v3_ca_create -new -days $days -batch -config $NetSNMP::Cert::SSLCFG -keyout '$outKey'";
+  $cmd .= " -nodes";
+
+  if (defined $ca) {
+    # we have to gen a csr and then sign it, must preserve CA:TRUE
+    my $outCsr = "$NetSNMP::Cert::NEWCRTDIR/$tag.csr";
+    NetSNMP::Cert::check_output_file($opts, $outCsr,
+				   $config->inherit('interactive'),
+				   $config->inherit('force'));
+    $cmd .= " -out '$outCsr'";
+
+    NetSNMP::Cert::dprint("genCa (gencsr): $cmd\n", $opts);
+    NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts);
+
+    my $ca_base = ::basename($ca, @NetSNMP::Cert::CRTSUFFIXES);
+    NetSNMP::Cert::dprint("ca_base: $ca_base\n", $opts);
+
+    # set command env
+    $env = $app->opensslEnv($cn, $days, ".ca/$ca_base");
+
+    $cmd = "$env openssl ca -extensions v3_ca_sign_ca -days $days -cert '$ca' -keyfile '$NetSNMP::Cert::PRIVDIR/$ca_base.key' -in '$outCsr' -batch -config $NetSNMP::Cert::SSLCFG -out '$outCrt'";
+
+    NetSNMP::Cert::dprint("genCa (signcsr): $cmd\n", $opts);
+    NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts);
+  } else {
+    $cmd .= " -x509 -out '$outCrt'";
+
+    NetSNMP::Cert::dprint("genCa: $cmd\n", $opts);
+    NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts);
+  }
+
+  NetSNMP::Cert::vprint("CA Generated:\n", $opts);
+  NetSNMP::Cert::vprint("  $outCrt\n", $opts);
+  NetSNMP::Cert::vprint("  $outKey\n", $opts);
+}
+
+sub genCert {
+  my $app = shift;
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $host = $config->inherit("host") || ::hostname();
+  my $email = $config->inherit("email") || getlogin() . "\@$host";
+  my $days = $config->inherit('days') || $config->inherit('crtDays') || $NetSNMP::Cert::DEFCRTDAYS;
+  my $cn = $config->inherit('cn') || $config->inherit('commonName');
+  my $ca = $config->inherit('with-ca');
+  my $cmd;
+
+  my $tag = $opts->{'tag'} || 'snmp';
+  $cn ||= (NetSNMP::Cert::is_server($tag) ? $host : $email);
+
+  my $env = $app->opensslEnv($cn, $days);
+
+  my $outCrt = "$NetSNMP::Cert::CRTDIR/$tag.crt";
+  my $outKey = "$NetSNMP::Cert::PRIVDIR/$tag.key";
+
+  NetSNMP::Cert::check_output_file($opts, $outCrt,
+				   $config->inherit('interactive'),
+				   $config->inherit('force'));
+  NetSNMP::Cert::check_output_file($opts, $outKey,
+				   $config->inherit('interactive'),
+				   $config->inherit('force'));
+
+  $cmd = "$env openssl req -extensions v3_user_create -new -days $days -keyout '$outKey' -batch -config $NetSNMP::Cert::SSLCFG";
+  $cmd .= " -nodes";
+
+  if (defined $ca) {
+    my $outCsr = "$NetSNMP::Cert::NEWCRTDIR/$tag.csr";
+    NetSNMP::Cert::check_output_file($opts, $outCsr,
+				   $config->inherit('interactive'),
+				   $config->inherit('force'));
+    $cmd .= " -out '$outCsr'";
+
+    NetSNMP::Cert::dprint("genCert (gencsr): $cmd\n", $opts);
+    NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts);
+
+    # XXX cleanup this temp CSR
+    my $ca_base = ::basename($ca, @NetSNMP::Cert::CRTSUFFIXES);
+    NetSNMP::Cert::dprint("ca_base: $ca_base\n", $opts);
+
+    # set command env
+    $env = $app->opensslEnv($cn, $days, ".ca/$ca_base");
+
+    $cmd = "$env openssl ca -extensions v3_ca_sign -days $days -cert '$ca' -keyfile '$NetSNMP::Cert::PRIVDIR/$ca_base.key' -in '$outCsr' -batch -config $NetSNMP::Cert::SSLCFG -out '$outCrt'";
+
+    NetSNMP::Cert::dprint("gencert (signcsr): $cmd\n", $opts);
+    NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts);
+  } else {
+    $cmd .= " -x509 -out '$outCrt'";
+
+    NetSNMP::Cert::dprint("genCert: $cmd\n", $opts);
+    NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts);
+  }
+
+  NetSNMP::Cert::vprint("Certificate Generated:\n", $opts);
+  NetSNMP::Cert::vprint("  $outCrt\n", $opts);
+  NetSNMP::Cert::vprint("  $outKey\n", $opts);
+}
+
+sub genCsr {
+  my $app = shift;
+  my $isCa = shift; # XXX - not implemented yet
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $host = $config->inherit("host") || ::hostname();
+  my $email = $config->inherit("email") || getlogin() . "\@$host";
+  my $days = $config->inherit('days') || $config->inherit('crtDays') || $NetSNMP::Cert::DEFCRTDAYS;
+  my $cn = $config->inherit('cn') || $config->inherit('commonName');
+  my $tag = $config->inherit('tag');
+  my $inCrt = $config->inherit('from-crt') || $config->inherit('fromCert');
+  my $outCsr;
+  my $csrKey;
+
+  if (defined $inCrt) {
+    $inCrt = NetSNMP::Cert::find_certs($opts, 'certs', $inCrt);
+    my $crt = ::basename($inCrt, @NetSNMP::Cert::CRTSUFFIXES);
+    $csrKey = "$NetSNMP::Cert::PRIVDIR/$crt.key";
+    $tag ||= $crt;
+  } else {
+    $tag ||= 'snmp';
+    $csrKey ||= "$NetSNMP::Cert::PRIVDIR/$tag.key";
+  }
+
+  $outCsr = "$NetSNMP::Cert::NEWCRTDIR/$tag.csr";
+
+  $cn ||= (NetSNMP::Cert::is_server($tag) ? $host : $email);
+
+  my $env = $app->opensslEnv($cn, $days);
+
+  NetSNMP::Cert::check_output_file($opts, $outCsr,
+				   $config->inherit('interactive'),
+				   $config->inherit('force'));
+  NetSNMP::Cert::check_output_file($opts, $csrKey,
+				   $config->inherit('interactive'),
+				   $config->inherit('force'));
+
+  my $cmd = (defined $inCrt ?
+  "$env openssl x509 -x509toreq -in $inCrt -out '$outCsr' -signkey '$csrKey' -nodes -days $days -batch -config $NetSNMP::Cert::SSLCFG" :
+  "$env openssl req -new -nodes -days $days -batch -keyout '$csrKey' -out '$outCsr' -config $NetSNMP::Cert::SSLCFG");
+
+  $cmd .= ($isCa ? " -extensions v3_ca_create" : " -extensions v3_user_create");
+
+  NetSNMP::Cert::dprint("genCsr: $cmd\n", $opts);
+
+  NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts);
+
+  NetSNMP::Cert::vprint("Certificate Signing Request Generated:\n", $opts);
+  NetSNMP::Cert::vprint("  $outCsr\n", $opts);
+  NetSNMP::Cert::vprint("  $csrKey\n", $opts);
+}
+
+sub signCsr {
+  my $app = shift;
+  my $isCa = shift;
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $host = $config->inherit("host") || ::hostname();
+  my $email = $config->inherit("email") || getlogin() . "\@$host";
+  my $days = $config->inherit('days') || $config->inherit('crtDays') || $NetSNMP::Cert::DEFCRTDAYS;
+  my $cn = $config->inherit('cn') || $config->inherit('commonName');
+  my $tag = $config->inherit('tag') || 'snmp';
+  my $install = $config->inherit('install');
+
+  $cn = (NetSNMP::Cert::is_server($tag) ? $host : $email);
+
+  my $ca = $opts->{'with-ca'};
+  NetSNMP::Cert::dprint("ca: $ca\n", $opts);
+  my $ca_base = ::basename($ca, @NetSNMP::Cert::CRTSUFFIXES);
+  NetSNMP::Cert::dprint("ca_base: $ca_base\n", $opts);
+  my $ca_key = "$NetSNMP::Cert::PRIVDIR/$ca_base.key"; 
+
+  my $csr = $opts->{'csr'};
+  NetSNMP::Cert::dprint("csr: $csr\n", $opts);
+  my $csr_base = ::basename($csr, @NetSNMP::Cert::CRTSUFFIXES);
+  NetSNMP::Cert::dprint("csr_base: $csr_base\n", $opts);
+  my $outdir = ($install ? $NetSNMP::Cert::CRTDIR : $NetSNMP::Cert::NEWCRTDIR);
+  my $outCrt = "$outdir/$csr_base.crt";
+
+  my $env = $app->opensslEnv($cn, $days, ".ca/$ca_base");
+
+  NetSNMP::Cert::check_output_file($opts, $outCrt,
+				   $config->inherit('interactive'),
+				   $config->inherit('force'));
+
+  # XXX - handle keyfile search??
+  my $cmd = "$env openssl ca -batch -days $days -extensions v3_ca_sign -cert '$ca' -keyfile '$ca_key' -in '$csr' -out '$outCrt' -config $NetSNMP::Cert::SSLCFG";
+
+  # $cmd .= ($isCa ? " -extensions v3_ca_sign_ca" : " -extensions v3_ca_sign");
+
+  NetSNMP::Cert::dprint("signCsr: $cmd\n", $opts);
+
+  NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts);
+
+  NetSNMP::Cert::vprint("Signed Certificate Signing Request:\n", $opts);
+  NetSNMP::Cert::vprint("  $csr\n", $opts);
+  NetSNMP::Cert::vprint("with CA:\n", $opts);
+  NetSNMP::Cert::vprint("  $ca\n", $opts);
+  NetSNMP::Cert::vprint("  $ca_key\n", $opts);
+  NetSNMP::Cert::vprint("Generated Certificate:\n", $opts);
+  NetSNMP::Cert::vprint("  $outCrt\n", $opts);
+}
+
+sub show {
+  my $app = shift;
+  my $type = shift || 'certs';
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $stag = shift(@{$opts->{'cmdargs'}});
+  my $fmt = NetSNMP::Cert::x509_format($opts) || '-subject';
+  my $brief = $config->inherit('brief');
+  my $output;
+  my $cmd;
+
+  my $cwd = ::getcwd();
+  NetSNMP::Cert::dprint("show ($cwd):$type:$stag:$fmt\n", $opts);
+  NetSNMP::Cert::vprint("$opts->{'tlsdir'}:\n", $opts) unless $brief;
+
+  foreach my $c (NetSNMP::Cert::find_certs($opts, $type, $stag)) {
+    print "\n$c:\n" unless $brief;
+    $cmd = "openssl x509 -in '$c' -noout $fmt";
+    NetSNMP::Cert::dprint("show: $cmd\n", $opts);
+
+    $output = `$cmd`; chomp $output;
+    NetSNMP::Cert::vwarn("show-$type failed ($?): $output\n", $opts) if $?;
+
+    $output =~ s/^[^\n=]+=// if $brief;
+
+    print "$output\n";
+    print "\n" unless $brief;
+  }
+}
+
+sub import_file {
+  my ($file, $suffix, $targ, $rdir, $tag) = @_;
+
+  if (NetSNMP::Cert::is_url($file)) {
+    if ($NetSNMP::Cert::haveUserAgent) {
+
+      require File::Temp;
+      import File::Temp qw(tempfile);
+
+      my ($fh, $newfilename) = tempfile(SUFFIX => $suffix);
+      return if (!$fh || !$newfilename);
+      my $ua = LWP::UserAgent->new;
+      my $response = $ua->get($file);
+      if ($response->is_success) {
+	print $fh $response->decoded_content();
+      } else {
+	NetSNMP::Cert::vwarn("failed to download a certificate from $file");
+	return;
+      }
+      $fh->close;
+      $file = $newfilename;
+    } else {
+      NetSNMP::Cert::vwarn("LWP::UserAgent not installed: unable to import certificate");
+      return;
+    }
+  }
+
+  $file = NetSNMP::Cert::fq_rel_path($file, $rdir);
+  die("file unreadable: $file\n") unless -r $file;
+
+
+  if (! $targ) {
+    $targ = (NetSNMP::Cert::is_ca_cert($file) ? $NetSNMP::Cert::CADIR : $NetSNMP::Cert::CRTDIR);
+  }
+
+  $targ .= "/" . $tag . $suffix if ($tag);
+  ::copy($file, $targ);
+}
+
+
+sub import {
+  my $app = shift;
+  my $config = $app->{'config'};
+  my $opts = $app->{'OPTS'};
+  my $carg = shift(@{$opts->{'cmdargs'}});
+  my $karg = shift(@{$opts->{'cmdargs'}});
+  my $targ;
+
+  if (not defined $carg) {
+    NetSNMP::Cert::vwarn("import: no certificate supplied\n", $opts);
+    NetSNMP::Cert::usage(1);
+  }
+
+  import_file($carg, '.crt', '',,
+	      $opts->{'rdir'}, $opts->{'tag'});
+
+  return unless defined $karg;
+
+  import_file($karg, '.key', 'private',,
+	      $opts->{'rdir'}, $opts->{'tag'});
+}
+
+
+package NetSNMP::Cert::Config;
+use vars qw(@ISA);
+@ISA = qw(NetSNMP::Cert::Obj);
+
+sub new {
+  my $class = shift;
+  my $parent = shift;
+  my $this = $class->SUPER::new('config', $parent);
+
+  bless($this, $class);
+}
+
+sub parse {
+  my $config = shift;
+  my $app = $config->{'APP'};
+  my $opts = $app->{'OPTS'};
+  my $cfgfile = shift;
+  $cfgfile ||= $opts->{'cfgfile'};
+
+  return '0 but true' if $config->{'PARSED'};
+  return '0 but true' unless defined $cfgfile;
+
+  open( CONFIG, "<$cfgfile" )
+    or die "error - could not open configuration file: $cfgfile";
+
+  while (<CONFIG>) {
+    next if /^\s*#/ or /^\s*$/;
+
+    if (/^\s*(\w+)(?:\(([\)\(\d\,\.]+)\))?\s*=\s*{/) {
+      my $obj = $1;
+
+      NetSNMP::Cert::dprint("object: $obj ($2) = {\n", $opts);
+      die "error - identity: indices not supported: $2" if defined $2;
+
+      # Found an object.
+      if ( $obj eq 'identity' ) {
+	my $identity = NetSNMP::Cert::Identity::parse(*CONFIG, $config);
+	my $id = $identity->{'id'};
+	die "error - identity: 'id' not defined" unless defined $id;
+	die "error - identity: duplicate '$id'" if exists $config->{$obj}{$id};
+	$config->{$obj}{$id} = $identity;
+      } else {
+        die "error - unrecognized object ($1) at scope (config)";
+      }
+    } elsif (/^\s*(\w+)\s*=?\s*(.*?)\s*$/) {
+      my $key = $1;
+      my $val = $2;
+
+      $val = $config->resolve($val) if $val =~ /\$\w+|\&\{.*?\}/;
+      # Found a symbol.
+      NetSNMP::Cert::dprint("  $key = $val\n", $opts);
+      if ($key eq 'subjectAltName' or $key eq 'san') {
+	push(@{$config->{$key}}, $val);
+      } elsif ( defined $config->{$key} ) {
+        die "error - duplicate symbol $key";
+      } else {
+        $config->{$key} = (defined $val ? NetSNMP::Cert::map_bool($val) : 1 );
+      }
+    } elsif (/^\s*env\s*(\w+=\S+)\s*$/) {
+      # Found an environment variable.
+      NetSNMP::Cert::dprint("$&\n", $opts);
+      push(@{$config->{'env'}}, $1);
+    } else {
+      die("error in config file [$cfgfile:line $.]");
+    }
+  }
+
+  NetSNMP::Cert::dprint("end parse config\n", $opts);
+  close(CONFIG);
+
+  # augment with any config directives supplied in opts
+  foreach my $cfg (@{$opts->{'config'}}) {
+    NetSNMP::Cert::dprint("augmenting config: $cfg\n", $opts);
+
+    $config->autoSet($1, (defined($2) ? $2 : 1 ))
+      if $cfg =~ /^\s*(\w+)\s*=?\s*(.*?)\s*$/;
+  }
+  $config->autoSet('PARSED', 1);
+  return $config->{'PARSED'};
+}
+
+package NetSNMP::Cert::Identity;
+use vars qw(@ISA);
+@ISA = qw(NetSNMP::Cert::Obj);
+
+sub new {
+  my $class = shift;
+  my $this = shift || $class->SUPER::new('identity', @_);
+  my $ind = $this->{'INDEX'} || 1;
+
+  $this->autoSet('name', "$this->{type}.$ind") unless exists $this->{'name'};
+
+  $this->autoSet('LOG','') unless exists $this->{'LOG'};
+  $this->autoSet('TTY_LOG','') unless exists $this->{TTY_LOG};
+
+  bless($this, $class);
+}
+
+
+sub parse {
+  my $FILE = shift;
+  my $parent = shift;
+  my $opts = $parent->inherit('OPTS');
+  my $identity = new NetSNMP::Cert::Obj('identity', $parent);
+
+  NetSNMP::Cert::dprint("parse identity\n", $opts);
+
+  while (<$FILE>) {
+    next if /^\s*#/ or /^\s*$/;
+
+    if (/^\s*(\w+)\s*=\s*{/) {
+      # Found an object.
+      die "error - can't have nested $1";
+    } elsif (/^\s*(\w+)\s*=?\s*(.*?)\s*$/) {
+      my $key = $1;
+      my $val = $2;
+
+      # Found a symbol.
+      NetSNMP::Cert::dprint("  $key = $val\n", $opts);
+
+      $val = $identity->resolve($val) if $val =~ /\$\w+|\&\{.*?\}/;
+
+      if ( $key eq 'subjectAltName' or $key eq 'san') {
+	push(@{$identity->{$key}}, $val);
+      } elsif ( defined $identity->{$key} ) {
+        die "error - duplicate symbol $key";
+      } else {
+        $identity->{$key} = (defined $val ? NetSNMP::Cert::map_bool($val) : 1 );
+      }
+    } elsif (/\s*\}\s*\;/) {
+      # End of definition.
+      NetSNMP::Cert::dprint("end parse identity\n", $opts);
+      return new NetSNMP::Cert::Identity($identity);
+    } else {
+      die("error in config file [$opts->{cfgfile}:line $.]");
+    }
+  }
+  die "error - unexpected end of conf file";
+}
+
+package main;
+
+my $app = new NetSNMP::Cert::App();
+$app->init(@ARGV);
+$app->run();
+
+__END__
+=pod
+
+=head1   NAME
+
+net-snmp-cert - Net-SNMP Certificate Management Tool
+
+=head1   SYNOPSIS
+
+=over
+
+=item $ net-snmp-cert genca -I --cn ca-Net-SNMP
+
+=item $ net-snmp-cert gencert -I -t snmpapp --with-ca ca-Net-SNMP
+
+=item $ net-snmp-cert gencert -I -t snmpd --cn net-snmp.org
+
+=item $ net-snmp-cert showcas
+
+=item $ net-snmp-cert showcerts
+
+=back
+
+=head1   DESCRIPTION
+
+net-snmp-cert creates, signs, installs and displays X.509
+certificates used in the operation of Net-SNMP/(D)TLS.
+
+=head1   SYNTAX
+
+=over
+
+=item net-snmp-cert [--help|-?]
+
+=item net-snmp-cert [--version|-V]
+
+=item net-snmp-cert genca    [<flags>] [<dn-opts>] [--with-ca <ca>]
+
+=item net-snmp-cert gencert  [<flags>] [<dn-opts>] [--with-ca <ca>]
+
+=item net-snmp-cert gencsr   [<flags>] [<dn-opts>] [--from-crt <crt>]
+
+=item net-snmp-cert signcsr  [<flags>] [--install] --csr <csr> --with-ca <ca>
+
+=item net-snmp-cert showca   [<flags>] [<format-opts>] [<file>|<search-tag>]
+
+=item net-snmp-cert showcert [<flags>] [<format-opts>] [<file>|<search-tag>]
+
+=item net-snmp-cert import   [<flags>] <file|url> [<key>]
+
+=back
+
+=head1   COMMANDS
+
+=over
+
+=item genca
+
+generate a signed CA certificate suitable for signing other
+certificates. default: self-signed unless --with-ca <ca> supplied
+
+=item gencert
+
+generate a signed certificate suitable for identification, or
+validation. default: self-signed unless --with-ca <ca> supplied
+
+=item gencsr
+
+generate a certificate signing request. will create a new
+key and certificate unless --from-crt <crt> supplied
+
+=item signcsr
+
+sign a certificate signing request specified by --csr <csr>
+with the CA certificate specified by --with-ca <ca>
+
+=item import
+
+import an identity or CA certificate, optionally import <key>
+if an URL is passed, will attempt to import certificate from site
+
+=item showca, showcert
+
+show CA or identity certificate(s). may pass fully qualified
+file or directory name, or a search-tag which prefix matches
+installed CA or identity certificate file name(s)
+see FORMAT OPTIONS to specify output format
+
+
+=back
+
+=head1   FLAGS
+
+=over
+
+=item -?, --help            -- show this text and exit
+
+=item -V, --version         -- show version string and exit
+
+=item -D, --debug           -- raise debug level (-D -D for higher level)
+
+=item -F, --force           -- force overwrite of existing output files
+
+=item -I, --nointeractive   -- non-interactive run (default interactive)
+
+=item -Q, --noverbose       -- non-verbose output (default verbose)
+
+=item -C, --cfgdir   <dir>  -- configuration dir (see man(5) snmp_config)
+
+=item -T, --tlsdir   <dir>  -- root for cert storage (default <cfgdir>/tls)
+
+=item -f, --cfgfile  <file> -- config (default <cfgdir>/net-snmp-cert.conf)
+
+=item -i, --identity <id>   -- identity to use from config
+
+=item -t, --tag      <tag>  -- application tag (default 'snmp')
+
+=item --<cfg-param>[=<val>] -- additional config params
+
+=back
+
+=head1   CERTIFICATE OPTIONS (<cert-opts>)
+
+=over
+
+=item -a, --with-ca <ca>    -- CA certificate used to sign request
+
+=item -A, --ca <ca>         -- explicit output CA certificate
+
+=item -r, --csr <csr>       -- certificate signing request
+
+=item -x, --from-crt <crt>  -- input certificate for current operation
+
+=item -X, --crt <crt>       -- explicit output certificate
+
+=item -y, --install         -- install result in local repository
+
+=back
+
+=head1   DISTINGUISHED NAME OPTIONS (<dn-opts>)
+
+=over
+
+=item -e, --email <email>       -- email name
+
+=item -h, --host <host>         -- DNS host name, or IP address
+
+=item -d, --days <days>         -- number of days certificate is valid
+
+=item -n, --cn <cn>             -- common name (CN)
+
+=item -o, --org <org>           -- organiztion name
+
+=item -u, --unit <unit>         -- organiztion unit name
+
+=item -c, --country <country>   -- country code (e.g., US)
+
+=item -p, --province <province> -- province name (synomynous w/ state)
+
+=item -p, --state <state>       -- state name (synomynous w/ province)
+
+=item -l, --locality <locality> -- locality name (e.g, town)
+
+=item -s, --san <san>           -- subjectAltName, repeat for each <san>
+
+=over 2
+
+=item    <san> value format (<FMT>:<VAL>):
+
+=over 3
+
+=item       dirName:/usr/share/snmp/tls
+
+=item       DNS:net-snmp.org
+
+=item       email:admin@net-snmp.org
+
+=item       IP:192.168.1.1
+
+=item       RID:1.1.3.6
+
+=item       URI:http://net-snmp.org
+
+=back
+
+=back
+
+=back
+
+=head1   FORMAT OPTIONS (<format-opts>)
+
+=over
+
+=item --brief        -- minimized output (values only where applicable)
+
+=item --text         -- full text description
+
+=item --subject      -- subject description
+
+=item --subject_hash -- hash of subject for indexing
+
+=item --issuer       -- issuer description
+
+=item --issuer_hash  -- hash of issuer for indexing
+
+=item --fingerprint  -- SHA1 digest of DER
+
+=item --serial       -- serial number
+
+=item --modulus      -- modulus of the public key
+
+=item --dates        -- start and end dates
+
+=item --purpose      -- displays allowed uses
+
+=item --C            -- C code description
+
+=back
+
+=head1   OPERATIONAL NOTES
+
+
+=head2   Interactive Mode
+
+The application operates in interactive mode by default. In this mode
+basic operations of offered and required input is queried through the
+command line. 
+
+Typical <tab> completion is provided when possible and field specific
+help may be obtained by entering '?'.
+
+To turn off interactive mode, supply '--nointeractive' or '-I' on the
+initial command line. Equivalantly, 'interactive = false' maybe placed
+in the configuration file (see below).
+
+=head2   Configuration
+
+A configuration file may be supplied on the command line or found in a
+default location (<snmpconfpath>/net-snmp-cert.conf). This file may
+contain configuration parameters equivalent to those supplied on the
+command line and effectively provides default values for these
+values. If a command line parameter is supplied it will override the
+value in the config file. If neither is present then an application
+value will be used.
+
+=head2   Certificate Naming
+
+Filenames of created certificates, as stored in the configuration
+directory, are chosen in the following manner. If and application tag
+is supplied, it will be used as the basename for the certificate and
+key generated. Otherwise, for CA certificates, the basename will be
+derived from the Common Name. For non-CA certificates the application
+tag defaults to 'snmp' which will then be used as the basename of the
+certificate and key.
+
+=head1   EXAMPLES
+
+=over
+
+=item net-snmp-cert genca --cn ca-net-snmp.org --days 1000
+
+=item net-snmp-cert genca -f .snmp/net-snmp-cert.conf -i nocadm
+
+=item net-snmp-cert gencert -t snmpd --cn host.net-snmp.org
+
+=item net-snmp-cert gencsr -t snmpapp
+
+=item net-snmp-cert signcsr --csr snmpapp --with-ca ca-net-snmp.org
+
+=item net-snmp-cert showcerts --subject --issuer --dates snmpapp
+
+=item net-snmp-cert showcas --fingerprint ca-net-snmp.org --brief
+
+=item net-snmp-cert import ca-third-party.crt
+
+=item net-snmp-cert import signed-request.crt signed-request.key
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (c) 2010 Cobham Analytic Solutions - All rights reserved.
+Copyright (c) 2010 G. S. Marzot -  All rights reserved.
+
+=head1   AUTHOR
+
+G. S. Marzot (marz@users.sourceforge.net)
+
+=cut
+

Property changes on: net-snmp-5.9-new/local/net-snmp-cert
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: net-snmp-5.9-new/local
===================================================================
--- net-snmp-5.9-new/local	(nonexistent)
+++ net-snmp-5.9-new/local	(revision 5)

Property changes on: net-snmp-5.9-new/local
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: net-snmp-5.9-new
===================================================================
--- net-snmp-5.9-new	(nonexistent)
+++ net-snmp-5.9-new	(revision 5)

Property changes on: net-snmp-5.9-new
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: .
===================================================================
--- .	(nonexistent)
+++ .	(revision 5)

Property changes on: .
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~