Клиент API Яндекс.Почты для домена
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

409 lines
10 KiB

#!/usr/local/bin/perl
# WANT_JSON
# yamd - client Yandex.Mail for Domain API
# Run as stand-alone application or as Ansible module
#
# Copyright 2018 Sergey Kiselev <root@digital-freak.ru>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
use strict;
use warnings;
our $VERSION = '0.1';
use File::Basename;
use Encode qw/encode/;
use YAML::XS qw/Load LoadFile/;
use JSON::XS;
use Types::Serialiser;
use LWP::UserAgent;
use HTTP::Request;
our $api_url = "https://pddimp.yandex.ru/api2/admin";
our $api_requests = Load(<<EOYAML
---
default_content_type: 'application/x-www-form-urlencoded'
deputy:
list:
query_type: 'GET'
parameters:
required: [domain]
add:
query_type: 'POST'
parameters:
required: [domain, login]
delete:
query_type: 'POST'
parameters:
required: [domain, login]
dkim:
status:
query_type: 'GET'
parameters:
required: [domain]
disable:
query_type: 'POST'
parameters:
required: [domain]
enable:
query_type: 'POST'
parameters:
required: [domain]
dns:
list:
query_type: 'GET'
parameters:
required: [domain]
add:
query_type: 'POST'
parameters:
required: [domain, type, content]
del:
query_type: 'POST'
parameters:
required: [domain, record_id]
edit:
query_type: 'POST'
parameters:
required: [domain, record_id]
domain:
details:
query_type: 'GET'
parameters:
required: [domain]
domains:
query_type: 'GET'
registration_status:
query_type: 'GET'
parameters:
required: [domain]
delete:
query_type: 'POST'
parameters:
required: [domain]
register:
query_type: 'POST'
parameters:
required: [domain]
logo:
check:
query_type: 'GET'
parameters:
required: [domain]
del:
query_type: 'POST'
parameters:
required: [domain]
set:
content_type: 'multipart/form-data'
query_type: 'POST'
parameters:
required: [domain, file]
settings:
set_country:
query_type: 'POST'
parameters:
required: [domain, country]
email:
counters:
query_type: 'GET'
parameters:
required: [domain, login]
list:
query_type: 'GET'
parameters:
required: [domain]
add:
query_type: 'POST'
parameters:
required: [domain, login, password]
del:
query_type: 'POST'
parameters:
required: [domain, login]
edit:
query_type: 'POST'
parameters:
required: [domain, login]
get_oauth_token:
query_type: 'POST'
parameters:
required: [domain, login]
ml:
list:
query_type: 'GET'
parameters:
required: [domain]
subscribers:
query_type: 'GET'
parameters:
required: [domain, maillist]
add:
query_type: 'POST'
parameters:
required: [domain, maillist]
del:
query_type: 'POST'
parameters:
required: [domain, maillist]
get_can_send_on_behalf:
query_type: 'POST'
parameters:
required: [domain, maillist, subscriber]
set_can_send_on_behalf:
query_type: 'POST'
parameters:
required: [domain, maillist, subscriber, can_send_on_behalf]
subscribe:
query_type: 'POST'
parameters:
required: [domain, maillist, subscriber]
unsubscribe:
query_type: 'POST'
parameters:
required: [domain, maillist, subscriber]
import:
check_imports:
query_type: 'GET'
parameters:
required: [domain]
check_settings:
query_type: 'GET'
parameters:
required: [domain, server, port, method, ssl]
start_import_file:
content_type: 'multipart/form-data'
query_type: 'POST'
parameters:
required: [domain, file, server, port, method, ssl]
start_one_import:
query_type: 'POST'
parameters:
required: [domain, server, port, method, ssl, ext-login, ext-password]
stop_all_imports:
query_type: 'POST'
parameters:
required: [domain]
EOYAML
);
our $task_file = shift or die "Usage: " . basename($0) . "<file.yml\n";
our $task = undef;
{
my ($filename, $path, $suffix) = fileparse($task_file);
if ($path =~ 'ansible' && $filename eq 'args') {
# Calling from Ansible with args in the JSON-file
open(my $fh, "<", $task_file);
$task = decode_json(<$fh>);
close $fh;
} else {
# Stand-alone running with args in the YAML-file
$task = %{LoadFile($task_file)}{yamd}
or die "Unable to open file '" . $task_file ."'\n";
}
}
# Checking general mandatory parameters of the request
foreach my $check ("query", "service", "token") {
if (!defined $task->{$check}) {
die "'" . $check . "' is undefined in '" . $task_file . "'\n";
} else {
if (!ref($task->{$check})) {
if ($task->{$check} =~ /^-?\d+\.?\d*/) {
die "Incorrect value of '" . $check . "' in '" . $task_file . "'\n";
}
} else {
die "Incorrect value of '" . $check . "' in '" . $task_file . "'\n";
}
}
}
my $request_url = "";
my $request_type = "";
my $request_content_type = "";
my $service = $task->{service};
my $sub_service = $task->{sub_service};
my $query = $task->{query};
my $options = $task->{options};
my $api_request = undef;
my @check_list;
# Checking mandatory parameters of the request
if (defined $sub_service) {
$api_request = $api_requests->{$service}->{$sub_service}->{$query};
} else {
$api_request = $api_requests->{$service}->{$query};
}
if (defined $api_request->{parameters}->{required}) {
@check_list = @{$api_request->{parameters}->{required}};
}
foreach my $check (@check_list) {
if (defined $task->{$check}) {
if (!ref($task->{$check})) {
if ($task->{$check} =~ /^-?\d+\.?\d*/) {
die "Incorrect value of '" . $check . "' in '" . $task_file . "'\n";
}
} else {
die "Incorrect value of '" . $check . "' in '" . $task_file . "'\n";
}
} elsif (defined $options->{$check}) {
if (!ref($options->{$check})) {
if ($options->{$check} =~ /^-?\d+\.?\d*/) {
die "Incorrect value of '" . $check . "' in '" . $task_file . "'\n";
}
} else {
die "Incorrect value of '" . $check . "' in '" . $task_file . "'\n";
}
} else {
die "'" . $check . "' is undefined in '" . $task_file . "'\n";
}
}
$request_url = $api_url . "/" . $service;
$request_url .= (defined $sub_service) ? "/" . $sub_service : "";
$request_url .= "/" . $query;
if ( $service eq "import" && $query eq "start_import_file" ) {
$request_url .= "?domain=" . $task->{domain};
foreach my $k (keys %{$task->{options}}) {
$request_url .= "&" . $k . "=" . $task->{options}->{$k};
}
}
if (defined $sub_service) {
$request_type = $api_request->{query_type};
$request_content_type = $api_request->{content_type};
} else {
$request_type = $api_request->{query_type};
$request_content_type = $api_request->{content_type};
}
if (!defined $request_content_type) {
$request_content_type = $api_requests->{default_content_type};
}
if (!defined $sub_service) {
$sub_service = "";
}
my $ua = new LWP::UserAgent;
my $request = new HTTP::Request($request_type => $request_url);
if ( $service eq "domain" &&
$sub_service eq "logo" &&
$query eq "set" ) {
{
my $boundary = 'X';
my @rand = ('a'..'z', 'A'..'Z');
for (0..14) { $boundary .= $rand[rand(@rand)]; }
$request->header(
'PddToken' => $task->{token},
'Content-type' => $request_content_type . "; boundary=" . $boundary
);
my $field = new HTTP::Message([
'Content-Disposition' => 'form-data; name="domain"',
'Content-Type' => 'text/plain; charset=utf-8'
]);
$field->add_content_utf8($task->{domain});
$request->add_part($field);
open(my $fh, '<', $task->{options}->{file});
my $file_content = new HTTP::Message([
'Content-Disposition' => 'form-data; name="file"; filename="logo"',
'Content-Type' => 'application/octet-stream'
]);
$file_content->add_content($_) while <$fh>;
$request->add_part($file_content);
close $fh;
}
} elsif ( $task->{service} eq "import" &&
$task->{query} eq "start_import_file" ) {
{
my $boundary = 'X';
my @rand = ('a'..'z', 'A'..'Z');
for (0..14) { $boundary .= $rand[rand(@rand)]; }
$request->header(
'PddToken' => $task->{token},
'Content-type' => $request_content_type . "; boundary=" . $boundary
);
open(my $fh, '<', $task->{options}->{file});
my $file_content = new HTTP::Message([
'Content-Disposition' => 'form-data; name="import_list_file"; filename="import_list_file"',
'Content-Type' => 'text/plain',
]);
$file_content->add_content($_) while <$fh>;
$request->add_part($file_content);
close $fh;
}
} else {
{
$request->header(
'PddToken' => $task->{token},
'Content-type' => $request_content_type
);
my $request_content = "";
if (defined $task->{domain}) {
$request_content = "domain=" . $task->{domain};
}
foreach my $k (keys %{$task->{options}}) {
$request_content .= "&" . $k . "=";
$request_content .= encode("utf8",$task->{options}->{$k});
}
$request->content($request_content);
}
}
my $response = $ua->request($request);
if ($response->is_success) {
print $response->content . "\n\n";
} else {
print $response->status_line . "\n\n";
}