App-pepper
view release on metacpan or search on metacpan
lib/App/pepper.pm view on Meta::CPAN
package App::pepper;
use App::pepper::EPPClient;
use App::pepper::Highlighter;
use File::Temp qw(tmpnam);
use Getopt::Long;
use Mozilla::CA;
use Net::EPP::Simple;
use List::Util qw(none);
use Pod::Usage;
use Term::ANSIColor;
use Term::ReadLine;
use Text::ParseWords;
use XML::LibXML;
use strict;
use constant {
SECURE_AUTHINFO_XMLNS => 'urn:ietf:params:xml:ns:epp:secure-authinfo-transfer-1.0',
};
use vars qw($VERSION);
our $VERSION = '1.0.0';
my $opt = {
'port' => 700,
'timeout' => 3,
'lang' => 'en',
};
my $result = GetOptions($opt,
'host=s',
'user=s',
'pass=s',
'newpw=s',
'exec=s',
'port=i',
'timeout=i',
'help',
'insecure',
'lang=s',
'debug',
'cert=s',
'key=s',
'login-security',
'nossl',
);
if (!$result || $opt->{'help'}) {
pod2usage(
'-verbose' => 99,
'-sections' => 'USAGE',
'-input' => __FILE__,
);
exit;
}
my $handlers = {
'timeout' => \&handle_timeout,
'ssl' => \&handle_ssl,
'host' => \&handle_host,
'port' => \&handle_port,
'credentials' => \&handle_credentials,
'id' => \&handle_id,
'pw' => \&handle_pw,
'newpw' => \&handle_newpw,
'connect' => \&handle_connect,
'login' => \&handle_login,
'logout' => \&handle_logout,
'hello' => \&handle_hello,
'check' => \&handle_check,
'info' => \&handle_info,
'poll' => \&handle_poll,
'help' => \&handle_help,
'send' => \&handle_send,
'BEGIN' => \&handle_begin,
'exit' => \&handle_exit,
'transfer' => \&handle_transfer,
'clone' => \&handle_clone,
'delete' => \&handle_delete,
'renew' => \&handle_renew,
'create' => \&handle_create,
'edit' => \&handle_edit,
'cert' => \&handle_cert,
'key' => \&handle_key,
'restore' => \&handle_restore,
'update' => \&handle_update,
};
our $term;
$term = Term::ReadLine->new('pepper') if (-t STDIN && -t STDOUT);
my $outfh = ($term ? \*STDOUT : \*STDERR);
my $prompt = 'pepper> ';
my $histfile = $ENV{'HOME'}.'/.pepper_history';
my $xml = XML::LibXML->new;
if ($term) {
$term->ReadHistory($histfile) if ('Term::ReadLine::Gnu' eq $term->ReadLine);
note('Welcome to pepper!');
note(color('yellow').'> For best results, install Term::ReadLine::Gnu <'.color('reset')) if ($term && 'Term::ReadLine::Gnu' ne $term->ReadLine);
}
my $epp = App::pepper::EPPClient->new(
'host' => '',
'connect' => undef,
'debug' => $opt->{'debug'},
'login' => undef,
'reconnect' => 0,
'verify' => ($opt->{'insecure'} ? undef : 1),
'ca_file' => Mozilla::CA::SSL_ca_file(),
'lang' => ($opt->{'lang'} ? $opt->{'lang'} : 'en'),
'appname' => sprintf('Pepper %s', $VERSION),
'login_security' => $opt->{'login-security'},
);
execute_command('ssl off') if ($opt->{'nossl'});
execute_command(sprintf('timeout %d', $opt->{'timeout'})) if ($opt->{'timeout'});
lib/App/pepper.pm view on Meta::CPAN
note('SSL enabled');
} elsif ($_[0] eq 'off') {
$epp->{'ssl'} = 0;
note('SSL disabled');
} else {
return error("Invalid SSL mode '%s'", $_[0]);
}
return 1;
}
sub handle_host {
if ($epp->connected) {
return error("Already connected");
} else {
$epp->{'host'} = $_[0];
note("Host set to %s", $_[0]);
return 1;
}
}
sub handle_port {
if ($epp->connected) {
return error("Already connected");
} else {
$epp->{'port'} = int($_[0]);
note("Port set to %d", $_[0]);
return 1;
}
}
sub handle_connect {
if ($epp->connected) {
return error("Already connected");
} elsif ($epp->{'host'} eq '') {
return error('No host specified');
} else {
note('Connecting to %s...', $epp->{'host'});
$epp->{'quiet'} = 1;
my $result = $epp->_connect(undef);
$epp->{'quiet'} = 0;
if ($result) {
note("Connected OK. Type 'hello' to see the greeting frame.");
} else {
error("Unable to connect: %s", $Net::EPP::Simple::Message);
}
return $result;
}
}
sub handle_credentials {
if ($_[0] eq '') {
return error('Missing client ID');
} elsif ($_[1] eq '') {
return error('Missing password');
} else {
handle_id($_[0]) && handle_pw($_[1]);
return 1;
}
}
sub handle_id {
if ($epp->authenticated) {
return error("Already authenticated");
} else {
$epp->{'user'} = shift;
note("User ID set to '%s'", $epp->{'user'});
}
}
sub handle_pw {
if ($epp->authenticated) {
return error("Already authenticated");
} else {
$epp->{'pass'} = shift;
note("Password set to '%s'", ('*' x length($epp->{'pass'})));
}
}
sub handle_newpw {
if ($epp->authenticated) {
return error("Already authenticated");
} else {
$epp->{'newPW'} = shift;
note("New password set to '%s'", ('*' x length($epp->{'newPW'})));
}
}
sub handle_login {
my $verbose = shift;
if (!$epp->connected) {
return handle_connect() && handle_login();
} elsif ($epp->authenticated) {
return error('Already logged in');
} elsif ($epp->{'host'} eq '') {
return error('No host specified');
} elsif ($epp->{'user'} eq '' || $epp->{'pass'} eq '') {
return error('No credentials specified');
} else {
note("Attempting to login as '%s'...", $epp->{'user'});
$epp->{'quiet'} = ($verbose ? 0 : 1);
my $result = $epp->_login;
$epp->{'quiet'} = 0;
if ($result) {
note('Logged in OK!');
} else {
error("%s%04d%s %s", color($Net::EPP::Simple::Code < 2000 ? 'green' : 'red'), $Net::EPP::Simple::Code, color('reset'), $Net::EPP::Simple::Message);
}
return $result;
}
}
sub handle_logout {
if (!$epp->authenticated) {
return error('Not logged in');
} else {
note('logging out');
$epp->{'quiet'} = 1;
my $result = $epp->logout;
$epp->{'quiet'} = 0;
note("%s%04d%s %s", color($Net::EPP::Simple::Code < 2000 ? 'green' : 'red'), $Net::EPP::Simple::Code, color('reset'), $Net::EPP::Simple::Message);
return $result;
}
}
sub handle_hello {
if (!$epp->connected) {
return error('Not connected');
} else {
return $epp->ping;
}
}
sub handle_check {
if (!$epp->authenticated) {
return error('Not connected');
} else {
my ($type, $id, @extra) = @_;
if ($type eq 'domain') {
return $epp->check_domain($id);
} elsif ($type eq 'claims') {
return handle_claims_check($id);
} elsif ($type eq 'fee') {
return handle_fee_check($id, @extra);
} elsif ($type eq 'host') {
return $epp->check_host($id);
} elsif ($type eq 'contact') {
lib/App/pepper.pm view on Meta::CPAN
my $frame;
eval { $frame = $xml->parse_string($buffer) };
if ($@ || !$frame) {
$@ =~ s/[\r\n]+$//g;
error("Unable to parse frame ($@)");
} else {
return $epp->request($frame);
}
}
}
sub handle_edit {
if (!$epp->connected) {
return error('Not connected');
} else {
my $file = tmpnam();
open(FILE, ">$file");
print FILE Net::EPP::Frame->new('command')->toString(2);
close(FILE);
my $cmd = ($ENV{'EDITOR'} || '/usr/bin/vi');
my ($cmd, @args) = split(/[ \t]+/, $cmd);
push(@args, $file);
if (0 != system($cmd, @args)) {
error("Command '$cmd' exited abnormally");
} else {
return send_file($file);
}
}
}
sub send_file {
my $file = shift;
my $frame;
eval { $frame = $xml->parse_file($file) };
if ($@ || !$frame) {
$@ =~ s/[\r\n]+$//g;
return error("Unable to parse '$file': $@");
} else {
return $epp->request($frame);
}
}
sub handle_help {
my $cmd = lc(shift || 'help');
my %map = (
'timeout' => 'SYNTAX/Connection Management',
'ssl' => 'SYNTAX/Connection Management',
'host' => 'SYNTAX/Connection Management',
'port' => 'SYNTAX/Connection Management',
'credentials' => 'SYNTAX/Session Management',
'id' => 'SYNTAX/Session Management',
'pw' => 'SYNTAX/Session Management',
'newpw' => 'SYNTAX/Session Management',
'connect' => 'SYNTAX/Connection Management',
'login' => 'SYNTAX/Session Management',
'logout' => 'SYNTAX/Session Management',
'hello' => 'SYNTAX/Session Management',
'check' => 'SYNTAX/Query Commands/Availability Checks',
'info' => 'SYNTAX/Query Commands/Object Information',
'poll' => 'SYNTAX/Session Management',
'help' => 'SYNTAX/Getting Help',
'send' => 'SYNTAX/Miscellaneous Commands',
'begin' => 'SYNTAX/Miscellaneous Commands',
'exit' => 'SYNTAX/Connection Management',
'transfer' => 'SYNTAX/Object Transfers',
'clone' => 'SYNTAX/Creating Objects',
'delete' => 'SYNTAX/Transform Commands',
'renew' => 'SYNTAX/Transform Commands',
'create' => 'SYNTAX/Creating Objects',
'create-domain' => 'SYNTAX/Creating Objects/Creating Domain Objects',
'create-host' => 'SYNTAX/Creating Objects/Creating Host Objects',
'create-contact' => 'SYNTAX/Creating Objects/Creating Contact Objects',
'edit' => 'SYNTAX/Miscellaneous Commands',
'cert' => 'SYNTAX/Connection Management',
'key' => 'SYNTAX/Connection Management',
'restore' => 'SYNTAX/Transform Commands',
'update' => 'SYNTAX/Object Updates',
'update-domain' => 'SYNTAX/Object Updates/Domain Updates',
'update-host' => 'SYNTAX/Object Updates/Host Updates',
'update-contact' => 'SYNTAX/Object Updates/Contact Updates',
);
print "\n";
pod2usage(
'-verbose' => 99,
'-sections' => $map{$cmd} || $map{'help'},
'-exitval' => 'NOEXIT',
'-input' => __FILE__,
);
}
sub handle_exit {
$epp->{'quiet'} = 1;
$epp->logout if ($epp->authenticated);
$epp->disconnect if ($epp->connected);
note('bye!');
exit;
}
sub handle_transfer {
if (!$epp->authenticated) {
return error('Not connected');
} else {
my ($type, $object, $cmd, $authinfo, $period) = @_;
return error("invalid object type '%s'", $type) if ($type ne 'domain' && $type ne 'contact');
return error("invalid command '%s'", $cmd) if ($cmd ne 'query' && $cmd ne 'request' && $cmd ne 'cancel' && $cmd ne 'approve' && $cmd ne 'reject');
( run in 1.363 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )