Mini Shell

Direktori : /opt/sharedrads/
Upload File :
Current File : //opt/sharedrads/cpanel-api

#!/usr/bin/perl
# encoding: utf-8
#
# author: Kyle Yetter
#

=begin

=head1 call-api

call-api - Change the basic configuration of a cPanel account.

=head1 SYNOPSIS

    call-api [options] I<function> I<arg-name>=I<val> ...
    call-api [options] -1 I<user> I<module> I<function> I<arg1> ... I<argN>
    call-api [options] -2 I<user> I<module> I<function> I<arg-name>=I<val> ...

=head1 DESCRIPTION

Run a cPanel remote API call and dump the server response in a variety of formats

See the following documenation pages for the cPanel API to learn more about
API calls and the functions they provide.

  http://docs.cpanel.net/twiki/bin/view/SoftwareDevelopmentKit/XmlApi
  http://docs.cpanel.net/twiki/bin/view/ApiDocs/Api1/WebHome
  http://docs.cpanel.net/twiki/bin/view/ApiDocs/Api2/WebHome

=head1 OPTIONS

=over 8

=item B<-1>, B<--modular-v1>

Specify the call as being a v1 module API function.

=item B<-2>, B<--module-v2>

Specify the call as being a v2 module API function.

=item B<-D>, B<--debug>

Print the WHM API URI sent to the server and dump the resulting data structure

=item B<-x>, B<--xml>

Run the call through cPanel's XML API and output the XML result

=item B<-j>, B<--json>

Run the call through cPanel's JSON API and output the raw JSON result

=item B<-y>, B<--yaml>

Run the call through cPanel's JSON API and dump the output data in YAML format

=item B<-b>, B<--bleach>, B<--no-color>

Do not use color escapes in the output

=item B<-h>, B<--help>

You know the drill

=item B<-v>, B<--version>

Ditto

=back

=cut



# to skip certificate verification
$ENV{'PERL_LWP_SSL_VERIFY_HOSTNAME'} = 0;
our $VERSION = 1.3;

use strict;
use LWP::UserAgent;
use URI;
use File::Basename;
use File::Slurp qw( slurp read_dir );
use Getopt::Long;
use JSON::Syck;
use Term::ANSIColor;
use Pod::Usage;

our $BASIC      = 0;
our $MODULAR_V1 = 1;
our $MODULAR_V2 = 2;
our $API_TYPE   = 'json';
our $FORMAT     = 'yaml';

# display extra debugging info if true
our $DEBUG = 0;
# if true, don't colorize output with ANSI escape sequences
our $BLEACH = 0;
# specifies whether this is a basic or modular API call
our $CALL_MODE = $BASIC;

# set to 1 if we had to generate an access key
our $CREATED_ACCESS_HASH = 0;

our $ACCESS_KEY;

END {
  #
  # If we had to create a temporary access hash to make this work,
  # make sure it gets trashed
  #
  if ( -f '/root/.accesshash' && $CREATED_ACCESS_HASH ) {
    unlink( '/root/.accesshash' ) or die( "failed to remove /root/.accesshash" );
  }

}


sub c($$) {
  my ( $str, $style ) = @_;
  $str = colored( $str, $style ) unless $BLEACH;
  return $str;
}

sub d {
  if ( $DEBUG ) {
    my ( $fmt, @params ) = @_;
    local $\ = "\n";

    my $message = c( sprintf( $fmt, @params ), 'cyan' );
    print STDERR "DEBUG: $message";
  }
}

sub access_key {
  if ( $ACCESS_KEY ) { return $ACCESS_KEY; }

  unless ( -f '/root/.accesshash' ) {
    $ENV{ REMOTE_USER } = 'root';
    `/usr/local/cpanel/bin/mkaccesshash`;
    delete $ENV{ REMOTE_USER };
    $CREATED_ACCESS_HASH = 1;
  }

  if ( -f '/root/.accesshash' ) {
    $ACCESS_KEY = slurp( "/root/.accesshash" ) or die "could not read /root/.accesshash: $!";
    $ACCESS_KEY =~ s/\n//g;
  } else {
    die "could not read /root/.accesshash: $!";
  }

  return $ACCESS_KEY;
}

sub load_json($) {
  my ( $source ) = @_;
  my $data;
  eval { $data = JSON::Syck::Load( $source ); };
  if ( $@ ) {
    die( $@ . "\nJSON SOURCE:\n" . $source );
  }
  return $data;
}


sub api_call($%) {
  my $function = shift;
  my %params = @_;

  my $uri = URI->new( "https://127.0.0.1:2087/$API_TYPE-api/$function" );
  $uri->query_form( %params );
  d "query URI: %s", $uri;

  my $auth     = "WHM root:" . access_key();
  my $ua       = LWP::UserAgent->new;
  my $request  = HTTP::Request->new( GET => "$uri" );
  $request->header( Authorization => $auth );
  my $response = $ua->request( $request );

  return $response->content;
}

sub modular_api_v1($$$@) {
  my $user   = shift;
  my $module = shift;
  my $func   = shift;
  my @args   = @_;
  my %opts   = (
    "cpanel_${API_TYPE}api_user"       => $user,
    "cpanel_${API_TYPE}api_module"     => $module,
    "cpanel_${API_TYPE}api_func"       => $func,
    "cpanel_${API_TYPE}api_apiversion" => 1
  );
  for my $i ( 0 .. $#args ) {
    $opts{ "arg-$i" } = $args[ $i ];
  }

  return api_call( 'cpanel', %opts );
}


sub modular_api_v2($$$%) {
  my $user   = shift;
  my $module = shift;
  my $func   = shift;

  my %opts = (
    "cpanel_${API_TYPE}api_user"       => $user,
    "cpanel_${API_TYPE}api_module"     => $module,
    "cpanel_${API_TYPE}api_func"       => $func,
    "cpanel_${API_TYPE}api_apiversion" => 2,
    @_
  );

  return api_call( 'cpanel', %opts );
}

sub parse_keyword_args(@) {
  my %data;
  for my $arg ( @_ ) {
    my ( $k, $v ) = split( /=/, $arg, 2 );
    defined( $v ) or $v = 1;
    $data{ $k } = $v;
  }
  return %data;
}

sub fail_with_help {
  my $message = shift;
  if ( $message ) {
    $message = c( $message, "red" );
  }
  pod2usage({
    -message => $message,
    -exitval => 1,
    -verbose => 2,
    -output  => \*STDERR
  });
}

GetOptions(
  '1|modular-v1' => sub {
    $CALL_MODE = $MODULAR_V1;
  },
  '2|modular-v2' => sub {
    $CALL_MODE = $MODULAR_V2;
  },
  'x|xml'     => sub {
    $API_TYPE = 'xml';
    $FORMAT   = 'xml';
  },
  'j|json'    => sub {
    $API_TYPE = 'json';
    $FORMAT   = 'json';
  },
  'y|yaml'    => sub {
    $API_TYPE = 'json';
    $FORMAT   = 'yaml';
  },
  'D|d|debug' => \$DEBUG,
  'b|bleach'  => \$BLEACH,
  'h|help' => sub { pod2usage( 0 ); },
  'v|version' => sub { print "$VERSION\n"; exit 0; }
);



my $result;
if ( $CALL_MODE == $BASIC ) {
  my $func = shift( @ARGV ) or fail_with_help( "no API function name provided" );
  my %params = parse_keyword_args( @ARGV );

  $result = api_call( $func, %params );

} elsif ( $CALL_MODE == $MODULAR_V1 ) {
  my $user = shift( @ARGV ) or fail_with_help( "no user name provided" );
  my $mod  = shift( @ARGV ) or fail_with_help( "no module name provided" );
  my $func = shift( @ARGV ) or fail_with_help( "no function name provided" );
  my @params = @ARGV;

  $result = modular_api_v1( $user, $mod, $func, @params );
} else {
  my $user = shift( @ARGV ) or fail_with_help( "no user name provided" );
  my $mod  = shift( @ARGV ) or fail_with_help( "no module name provided" );
  my $func = shift( @ARGV ) or fail_with_help( "no function name provided" );
  my %params = parse_keyword_args( @ARGV );

  $result = modular_api_v2( $user, $mod, $func, %params );
}

if ( $FORMAT eq 'xml' or $FORMAT eq "json" ) {
  print $result;
} elsif ( $FORMAT eq 'yaml' ) {
  eval {
    require YAML::Any;
    1;
  } or do {
    die "Cannot provide YAML output -- failed to load YAML::Any: $@";
  };

  print YAML::Any::Dump( load_json( $result ) );
}

Zerion Mini Shell 1.0