File Coverage

blib/lib/WebService/MIAB.pm
Criterion Covered Total %
statement 29 112 25.8
branch 0 30 0.0
condition 0 24 0.0
subroutine 10 24 41.6
pod 11 12 91.6
total 50 202 24.7


line stmt bran cond sub pod time code
1             package WebService::MIAB;
2              
3 1     1   69329 use 5.006;
  1         4  
4 1     1   6 use strict;
  1         2  
  1         20  
5 1     1   5 use warnings;
  1         3  
  1         53  
6              
7             =head1 NAME
8              
9             WebService::MIAB - manage emails (users) and alias addresses on a mail-in-a-box server
10              
11             =head1 VERSION
12              
13             version 0.02
14              
15             =cut
16              
17 1     1   495 use version;
  1         1939  
  1         6  
18             our $VERSION = '0.02';
19              
20 1     1   710 use Moo;
  1         11842  
  1         5  
21             with 'WebService::Client';
22              
23 1     1   2224 use LWP::UserAgent;
  1         50835  
  1         44  
24 1     1   555 use MIME::Base64;
  1         661  
  1         63  
25 1     1   7 use Carp qw(croak);
  1         2  
  1         48  
26 1     1   5 use re qw(is_regexp);
  1         9  
  1         167  
27              
28 1     1   740 use JSON;
  1         10258  
  1         6  
29             my $json = JSON->new->allow_nonref;
30              
31             #Alias_Address
32             has alias_get_uri => ( is => 'ro', default => '/mail/aliases?format=json' );
33             has alias_add_uri => ( is => 'ro', default => '/mail/aliases/add' );
34             has alias_remove_uri => ( is => 'ro', default => '/mail/aliases/remove' );
35              
36             #User_Address
37             has user_get_uri => ( is => 'ro', default => '/mail/users?format=json');
38             has user_add_uri => ( is => 'ro', default => '/mail/users/add' );
39             has user_remove_uri => ( is => 'ro', default => '/mail/users/remove' );
40              
41             #General
42             has host => (is => 'ro', required => 1);
43             has pass => ( is => 'ro', required => 1 );
44             has username => ( is => 'ro', required => 1 );
45             has '+base_url' => (
46             is => 'ro',
47             lazy => 1,
48             builder => sub {
49 0     0     my ($self) = @_;
50 0           return 'https://'.$self->host.'/admin';
51             },
52             );
53              
54             # custom deserializer needed as the MIAB webservice doesn't return JSON for post requests
55             has '+deserializer' => (
56             is => 'ro',
57             lazy => 1,
58             default => sub {
59             my $self = shift;
60             my $usejson = $self->json;
61             sub {
62             my ($res, %args) = @_;
63             my $decoded = $res;
64              
65             if ($res->request->uri =~ /json/) {
66             $decoded = $json->decode($res->content);
67             }
68             # else {
69             # # no json request - nothing to do
70             # }
71             return $decoded;
72             }
73             },
74             );
75              
76              
77             sub BUILD(){
78 0     0 0   my ($self) = @_;
79 0           my $empty = q{};
80 0           my $colon = q{:};
81 0           return $self->ua->default_header('Authorization' => 'Basic '.encode_base64($self->username.$colon.$self->pass , $empty));
82             }
83              
84              
85             =head1 SYNOPSIS
86              
87             use WebService::MIAB;
88              
89             my $miab = WebService::MIAB->new(
90             username => 'adminuser@domain.com',
91             pass => 'adminpasswd',
92             host => 'domain.com',
93             );
94              
95             my $users = $miab->get_users();
96             my $result = $miab>create_user({
97             email => 'bart.simpson@domain.com',
98             password => 'EatMyShorts',
99             });
100              
101             =head1 DESCRIPTION
102              
103             WebService::MIAB is a Perl module that manages user and alias addresses on a
104             Mail-in-a-box (MIAB) server.
105              
106             You can list, create and remove user and alias addresses.
107              
108             The API methods to the DNS system of MIAB is not implemented, yet.
109             Patches are welcome.
110              
111             =head1 METHODES
112              
113             =head2 User methods
114              
115             The user methods are to handle plain email adresses on a MIAB server.
116             Each email address is a user and has (limited) access to the MIAB server.
117              
118             =head3 get_users
119              
120             Returns all defined users in JSON format.
121              
122             my $users = $miab->get_users();
123              
124             =cut
125              
126              
127             sub get_users{
128 0     0 1   my ($self) = @_;
129              
130 0           my $result = $self->get($self->user_get_uri);
131              
132 0           return $result;
133             }
134              
135              
136             =head3 create_user
137              
138             Creates a new user account. Expects the email address and the password in
139             the parameter hash.
140             Returns the answer from the webservice.
141              
142             my $result = $miab->create_user({
143             email => 'bart.simpson@springfield.com',
144             password => 'EatMyShorts'
145             });
146              
147             =cut
148              
149              
150             sub create_user{
151 0     0 1   my ($self, $param) = @_;
152              
153             my $result = $self->post($self->user_add_uri,
154             {
155             email => $param->{email},
156             password => $param->{password},
157             },
158 0           headers => { 'Content-Type' => 'application/x-www-form-urlencoded' },
159             );
160              
161 0           return $result;
162             }
163              
164              
165             =head3 remove_user
166              
167             Removes the email user account. Expects the email address in the parameter hash.
168             Returns the answer from the webservice.
169              
170             my result = $miab->remove_user({
171             email => bart.simpson@springfield.com
172             });
173              
174             =cut
175              
176              
177             sub remove_user{
178 0     0 1   my ($self, $param) = @_;
179              
180             my $result = $self->post($self->user_remove_uri,
181             {
182             email => $param->{email},
183             },
184 0           headers => { 'Content-Type' => 'application/x-www-form-urlencoded' },
185             );
186              
187 0           return $result;
188             }
189              
190              
191             =head3 find_user
192              
193             Returns a list of users records matching its forward_to record
194             to the given email_pattern and domain_pattern. The pattern can be a string
195             that is evaluated to a regexp or a regexp.
196              
197             my @users = $miab->find_user('email_pattern', qr/example.com/)
198              
199             =cut
200              
201              
202             sub find_user {
203 0     0 1   my ($self, $email_pattern, $domain_pattern) = @_;
204              
205 0           my @result;
206 0 0         if ( !is_regexp($email_pattern) ) {
207 0           $email_pattern = qr/$email_pattern/mxs;
208             }
209 0 0 0       if ($domain_pattern && !is_regexp($domain_pattern)) {
210 0           $domain_pattern = qr/$domain_pattern/mxs;
211             }
212              
213 0           my $users = $self->get_users();
214 0           foreach ( @{$users} ) {
  0            
215 0 0 0       if (!$domain_pattern || $_->{'domain'} =~ $domain_pattern) {
216 0           foreach (@{$_->{'users'}}) {
  0            
217 0           my $value = $_->{'email'};
218              
219             # filter list
220             push( @result,
221             grep {
222 0 0         $_ && $_ =~ $email_pattern
223             } ($value && 'ARRAY' eq ref $value)
224 0 0 0       ? @{$value}
  0            
225             : ( $value )
226             );
227             }
228             }
229             }
230              
231 0           return @result;
232             }
233              
234              
235             =head2 Alias methods
236              
237             =head3 get_aliases
238              
239             Returns all aliases in JSON format.
240              
241             my $aliases = $miab->get_aliases();
242              
243             =cut
244              
245              
246             sub get_aliases{
247 0     0 1   my ($self) = @_;
248              
249 0           my $result = $self->get($self->alias_get_uri);
250              
251 0           return $result;
252             }
253              
254              
255             =head3 get_domains
256              
257             Returns a list of all domains used in alias adresses.
258              
259             my @domains = $miab->get_domains();
260              
261             =cut
262              
263              
264             sub get_domains{
265 0     0 1   my ($self) = @_;
266              
267 0           my $aliases = $self->get_aliases();
268              
269 0           my @result;
270             map {
271 0           push( @result, $_->{'domain'} );
272 0           } @{$aliases};
  0            
273              
274             # TODO - unify
275              
276 0           return @result;
277             }
278              
279              
280             =head3 find_forward_to_aliases
281              
282             Returns a list of alias records matching its forward_to record
283             to the given alias_pattern and domain_pattern. The pattern can be a string
284             that is evaluated to a regexp or a regexp.
285              
286             my @aliases = $miab->find_forward_to_aliases('alias_pattern', qr/example.com/);
287              
288             =cut
289              
290              
291             sub find_forward_to_aliases {
292 0     0 1   my ($self, $alias_pattern, $domain_pattern) = @_;
293              
294 0           return $self->_find_aliases_generic('forwards_to', $alias_pattern, $domain_pattern);
295             }
296              
297              
298             =head3 find_permitted_sender_aliases
299              
300             Returns a list of alias records matching its permitted_sender record
301             to the given alias_pattern and domain_pattern. The pattern can be a string
302             that is evaluated to a regexp or a regexp.
303              
304             my @aliases = $miab->find_permitted_sender_aliases('alias_pattern', qr/example.com/);
305              
306             =cut
307              
308              
309             sub find_permitted_sender_aliases {
310 0     0 1   my ($self, $alias_pattern, $domain_pattern) = @_;
311              
312 0           return $self->_find_aliases_generic('permitted_senders', $alias_pattern, $domain_pattern);
313             }
314              
315              
316             =head3 find_aliases
317              
318             Returns a list of alias records matching its address record the given
319             alias_pattern and domain_pattern. The pattern can be a string that is
320             evaluated to a regexp or a regexp.
321              
322             my @aliases = $miab->find_aliases('alias_pattern', qr/example.com/);
323              
324             =cut
325              
326              
327             sub find_aliases {
328 0     0 1   my ($self, $alias_pattern, $domain_pattern) = @_;
329              
330 0           return $self->_find_aliases_generic('address', $alias_pattern, $domain_pattern);
331             }
332              
333              
334             =head3 create_alias
335              
336             Creates a new alias address.
337              
338             $miab->create_alias({
339             address => 'bart.simpson@springfield.com',
340             forwards_to => ['lisa.simpson@springfield.com','Maggie.simpson@springfield.com'],
341             permitted_senders => ['Homer.simpson@springfield.com','Marge.simpson@springfield.com'],
342             });
343              
344             =cut
345              
346              
347             sub create_alias{
348 0     0 1   my ($self, $param) = @_;
349 0           my $empty = q{};
350 0           my $point = q{,};
351              
352 0 0 0       if (defined $param->{forwards_to} && ref $param->{forwards_to} ne 'ARRAY'){
353 0           $param->{forwards_to} = [$param->{forwards_to}];
354             }
355 0 0 0       if (defined $param->{permitted_senders} && ref $param->{permitted_senders} ne 'ARRAY'){
356 0           $param->{permitted_senders} = [$param->{permitted_senders}];
357             }
358              
359             my $forward_address_for_request = defined $param->{forwards_to}
360 0 0         ? join ($point, @{$param->{forwards_to}})
  0            
361             : $empty;
362             my $permitted_senders_for_request = defined $param->{permitted_senders}
363 0 0         ? join ($point, @{$param->{permitted_senders}})
  0            
364             : $empty;
365              
366             # One alias parameter must exist
367 0 0         if(!length($forward_address_for_request.$permitted_senders_for_request)){
368 0           croak 'forwards_to or permitted_sender are required';
369             }
370              
371             my $result = $self->post($self->alias_add_uri,
372             {
373             address => $param->{address},
374 0           forwards_to => $forward_address_for_request,
375             permitted_senders => $permitted_senders_for_request,
376             },
377             headers => { 'Content-Type' => 'application/x-www-form-urlencoded' },
378             );
379 0           return $result;
380             }
381              
382              
383             =head3 remove_alias
384              
385             Permanently deletes an alias address.
386              
387             $miab->remove_alias({
388             address => bart.simpson@springfield.com
389             });
390              
391             =cut
392              
393             sub remove_alias{
394 0     0 1   my ($self, $param) = @_;
395             my $result = $self->post($self->alias_remove_uri,
396             {
397             address => $param->{address},
398             },
399 0           headers => { 'Content-Type' => 'application/x-www-form-urlencoded' }
400             );
401              
402 0           return $result;
403             }
404              
405              
406             sub _find_aliases_generic {
407 0     0     my ($self, $param_name, $alias_pattern, $domain_pattern) = @_;
408 0           my @result;
409              
410             # force patterns to be a regex
411 0 0         if (!is_regexp($alias_pattern)) {
412 0           $alias_pattern = qr/$alias_pattern/mxs;
413             }
414 0 0 0       if ($domain_pattern && !is_regexp($domain_pattern)) {
415 0           $domain_pattern = qr/$domain_pattern/mxs;
416             }
417              
418             # get all aliases
419 0           my $aliases = $self->get_aliases();
420              
421             # filter
422 0           foreach (@{$aliases}) {
  0            
423 0 0 0       if (!$domain_pattern || $_->{'domain'} =~ $domain_pattern)
424             {
425             # either $domain_pattern is undefined (param not given)
426             # or the current domain matched the given $domain_pattern
427              
428 0           foreach (@{$_->{'aliases'}}) {
  0            
429 0           my $value = $_->{$param_name};
430              
431             # filter list
432             push( @result,
433             grep {
434 0 0         $_ && $_ =~ $alias_pattern
435             } ($value && 'ARRAY' eq ref $value)
436 0 0 0       ? @{$value}
  0            
437             : ( $value )
438             );
439             }
440             }
441             }
442 0           return @result;
443             }
444              
445             =head1 BUGS
446              
447             Please report any bugs or feature requests to C, or through
448             the web interface at L. I will be notified, and then you'll
449             automatically be notified of progress on your bug as I make changes.
450              
451              
452             =head1 SUPPORT
453              
454             You can find documentation for this module with the perldoc command.
455              
456             perldoc WebService::MIAB
457              
458              
459             You can also look for information at:
460              
461             =over 4
462              
463             =item * RT: CPAN's request tracker (report bugs here)
464              
465             L
466              
467             =item * AnnoCPAN: Annotated CPAN documentation
468              
469             L
470              
471             =item * CPAN Ratings
472              
473             L
474              
475             =item * Search CPAN
476              
477             L
478              
479             =back
480              
481              
482             =head1 AUTHORS
483              
484             Alexander Scneider C, Mario Minati
485             C.
486              
487             Currently maintained by Alexander Scneider C
488              
489              
490             =head1 LICENSE AND COPYRIGHT
491              
492             Copyright 2018 Alexander Scneider C, Mario Minati
493             C.
494              
495             This program is free software; you can redistribute it and/or modify it
496             under the terms of the the Artistic License (2.0). You may obtain a
497             copy of the full license at:
498              
499             L
500              
501             Any use, modification, and distribution of the Standard or Modified
502             Versions is governed by this Artistic License. By using, modifying or
503             distributing the Package, you accept this license. Do not use, modify,
504             or distribute the Package, if you do not accept this license.
505              
506             If your Modified Version has been derived from a Modified Version made
507             by someone other than you, you are nevertheless required to ensure that
508             your Modified Version complies with the requirements of this license.
509              
510             This license does not grant you the right to use any trademark, service
511             mark, tradename, or logo of the Copyright Holder.
512              
513             This license includes the non-exclusive, worldwide, free-of-charge
514             patent license to make, have made, use, offer to sell, sell, import and
515             otherwise transfer the Package with respect to any patent claims
516             licensable by the Copyright Holder that are necessarily infringed by the
517             Package. If you institute patent litigation (including a cross-claim or
518             counterclaim) against any party alleging that the Package constitutes
519             direct or contributory patent infringement, then this Artistic License
520             to you shall terminate on the date that such litigation is filed.
521              
522             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
523             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
524             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
525             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
526             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
527             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
528             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
529             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
530              
531              
532             =cut
533              
534             1; # End of WebService::MIAB