File Coverage

blib/lib/WWW/Yandex/PDD.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package WWW::Yandex::PDD;
2            
3 1     1   21557 use strict;
  1         3  
  1         39  
4 1     1   6 use warnings;
  1         2  
  1         55  
5            
6             our $VERSION = '0.05';
7            
8 1     1   1025 use LWP::UserAgent; # also required: Crypt::SSLeay or IO::Socket::SSL
  1         48153  
  1         28  
9 1     1   822 use LWP::ConnCache;
  1         1094  
  1         26  
10 1     1   352 use XML::LibXML;
  0            
  0            
11             use XML::LibXML::XPathContext; # explicit use is required in some cases
12             use URI::Escape;
13            
14             use WWW::Yandex::PDD::Error;
15            
16             use constant API_URL => 'https://pddimp.yandex.ru/';
17            
18             sub new
19             {
20             my $class = shift;
21             my %data = @_;
22            
23             my $self = {};
24            
25             bless $self, $class;
26            
27             return undef unless $self -> __init(\%data);
28            
29             return $self;
30             }
31            
32             sub __init
33             {
34             my $self = shift;
35             my $data = shift;
36            
37             return undef unless $data;
38             return undef unless $data -> {token};
39            
40             $self -> {token} = $data -> {token};
41            
42             $ENV{HTTPS_CA_FILE} = $data -> {cert_file} if ($data -> {cert_file});
43            
44             $self -> {ua} = new LWP::UserAgent;
45             $self -> {ua} -> conn_cache(new LWP::ConnCache);
46            
47             $self -> {parser} = new XML::LibXML;
48             $self -> {xpath} = new XML::LibXML::XPathContext;
49            
50             return 1;
51             }
52            
53             sub __reset_error
54             {
55             my $self = shift;
56            
57             $self -> {error} = undef;
58             $self -> {http_error} = undef;
59             }
60            
61             sub __set_error
62             {
63             my $self = shift;
64            
65             my ($code, $info, $is_http) = @_;
66            
67             $self -> __reset_error();
68            
69             if ($is_http)
70             {
71             $self -> {error} = { code => &WWW::Yandex::PDD::Error::HTTP_ERROR, info => undef };
72             $self -> {http_error} = { code => $code, info => $info };
73             }
74             else
75             {
76             $self -> {error} = { code => $code, info => $info };
77             }
78             }
79            
80             sub __handle_error
81             {
82             my $self = shift;
83            
84             $self -> __set_error( WWW::Yandex::PDD::Error::identify( $self -> {_error} ), $self -> {_error} );
85             }
86            
87             sub __handle_http_error
88             {
89             my $self = shift;
90            
91             $self -> __set_error( $self -> {r} -> code(),
92             $self -> {r} -> decoded_content(),
93             &WWW::Yandex::PDD::Error::HTTP_ERROR
94             );
95             }
96            
97             sub __unknown_error
98             {
99             my $self = shift;
100            
101             $self -> __set_error( &WWW::Yandex::PDD::Error::UNKNOWN_ERROR,
102             $self -> {r} -> decoded_content()
103             );
104            
105             return undef;
106             }
107            
108             sub __get_nodelist
109             {
110             my $self = shift;
111             my $xpath = shift;
112             my $xml = shift || $self -> {xml};
113            
114             return '' unless ($xpath and $xml); # TODO die
115            
116             return $self -> {xpath} -> findnodes($xpath, $xml);
117             }
118            
119             sub __get_node_text
120             {
121             my $self = shift;
122             my $xpath = shift;
123             my $xml = shift || $self -> {xml};
124            
125             return '' unless ($xpath and $xml); # TODO die
126            
127             return $self -> {xpath} -> findvalue($xpath, $xml);
128             }
129            
130             sub __get_node_array
131             {
132             my $self = shift;
133             my $xpath = shift;
134             my $xml = shift || $self -> {xml};
135            
136             return '' unless ($xpath and $xml); # TODO die
137            
138             $xpath =~ s/\/$//;
139            
140             return $self -> __get_nodelist( $xpath, $xml ) -> to_literal_list();
141             }
142            
143             sub __parse_response
144             {
145             my $self = shift;
146            
147             my $xml;
148            
149             if (defined $ENV{ALUCK_TRACE}) {
150             open(my $d, '>>', $ENV{ALUCK_TRACE}) || warn "error opening debug: $!\n";
151             print $d $self -> {r} -> decoded_content();
152             close($d);
153             }
154            
155             eval
156             {
157             $xml = $self -> {parser} -> parse_string( $self -> {r} -> decoded_content() );
158             };
159            
160             if ($@)
161             {
162             $self -> __set_error(&WWW::Yandex::PDD::Error::INVALID_RESPONSE);
163             return undef;
164             }
165            
166             unless ($xml)
167             {
168             $self -> __set_error(&WWW::Yandex::PDD::Error::INVALID_RESPONSE);
169             return undef;
170             }
171            
172             $self -> {xml} = $xml;
173            
174             for my $path (
175             '/page/error/@reason',
176             '/action/status/error',
177             '/action/domains/domain/logo/action-status/error',
178             '/action/domain/status'
179             )
180             {
181             if ( $self -> {_error} = $self -> __get_node_text($path) )
182             {
183             $self -> __handle_error();
184             return undef;
185             }
186             }
187            
188             if ( $self -> __get_node_text('/page/xscript_invoke_failed/@error') )
189             {
190             my $info = '';
191            
192             for ( qw{ error block method object exception } )
193             {
194             my $s = '/page/xscript_invoke_failed/@' . $_;
195            
196             $info .= $_ . ': "' . $self -> __get_node_text($s) . '" ';
197             }
198            
199             $self -> __set_error(&WWW::Yandex::PDD::Error::SERVICE_ERROR, $info);
200             return undef;
201             }
202            
203             return 1 unless ( $self -> {_error} );
204             }
205            
206             sub __make_request
207             {
208             my $self = shift;
209             my $url = shift;
210             my $type = shift || 'get';
211             # only for POST requests
212             my $post_content_type = shift;
213             my $post_content = shift;
214            
215             $self -> __reset_error();
216            
217             if ($type eq 'get') {
218             $self -> {r} = $self -> {ua} -> get($url);
219             } else {
220             $self -> {r} = $self -> {ua} -> post( $url, Content_Type => $post_content_type, Content => $post_content) ;
221             }
222            
223             unless ($self -> {r} -> is_success)
224             {
225             $self -> __handle_http_error();
226             return undef;
227             }
228            
229             return $self -> __parse_response();
230             }
231            
232             sub __simple_query {
233             my $self = shift;
234             my $url = shift;
235             my $returning_params = shift;
236            
237             return undef unless $self -> __make_request($url);
238            
239             if ($returning_params) {
240             my %result;
241             foreach my $param (keys %$returning_params) {
242             my $xpath = $returning_params->{$param};
243             if ($xpath =~ /\/$/) {
244             $result{$param} = $self -> __get_node_array( $xpath );
245             } else {
246             $result{$param} = $self -> __get_node_text( $xpath );
247             }
248             }
249            
250             return \%result;
251             } else {
252             return 1;
253             }
254             }
255            
256             sub get_last_error {
257             my $self = shift;
258            
259             my $error = undef;
260            
261             if ( $self -> {error} ) {
262             $error = $self -> {error};
263             } elsif ( $self -> {http_error} ) {
264             $error = $self -> {http_error};
265             }
266            
267             return $error;
268             }
269            
270             # DOMAINS
271            
272             sub domain_reg
273             {
274             my ($self, $domain) = @_;
275            
276             return $self -> __simple_query(
277             API_URL . 'api/reg_domain.xml?token=' . $self -> {token} . '&domain=' . $domain,
278             {
279             name => '/action/domains/domain/name/text()',
280             secret_name => '/action/domains/domain/secret_name/text()',
281             secret_value => '/action/domains/domain/secret_value/text()',
282             }
283             );
284             }
285            
286             sub domain_unreg
287             {
288             my ($self, $domain) = @_;
289            
290             return $self -> __simple_query(
291             API_URL . 'api/del_domain.xml?token=' . $self -> {token} . '&domain=' . $domain,
292             );
293             }
294            
295             sub domain_add_logo {
296             my $self = shift;
297             my $domain = shift;
298             my $file_name = shift; # jpg, gif, png < 2 Mb
299            
300             my $content = {
301             token => $self -> {token},
302             domain => $domain,
303             file => [ $file_name ],
304             };
305            
306             return undef unless $self -> __make_request(API_URL . 'api/add_logo.xml', 'post', 'multipart/form-data', $content);
307            
308             # FIXME: monicor.rusave_failed
309             return {
310             name => $self -> __get_node_text('/action/domains/domain/name/text()'),
311             logo_url => $self -> __get_node_text('/action/domains/logo/url/text()'),
312             };
313             }
314            
315             sub domain_del_logo {
316             my ($self, $domain) = @_;
317            
318             return $self -> __simple_query(
319             API_URL . 'api/del_logo.xml?token=' . $self -> {token} . '&domain=' . $domain,
320             {
321             name => '/action/domains/domain/name/text()',
322             }
323             );
324             }
325            
326             sub domain_set_default_user {
327             my ($self, $domain, $login) = @_;
328            
329             return $self -> __simple_query(
330             API_URL . 'api/reg_default_user.xml?token=' . $self -> {token} . '&domain=' . $domain . '&login=' . $login,
331             {
332             name => '/action/domains/domain/name/text()',
333             email => '/action/domains/domain/default-email/text()',
334             }
335             );
336             }
337            
338             sub domain_add_admin {
339             my $self = shift;
340             my $domain = shift;
341             my $login = shift; # should be a real mailbox from @yandex.ru; i.e. if you want to add foobar@yandex.ru - $login should be "foobar"
342            
343             return $self -> __simple_query(
344             API_URL . 'api/multiadmin/add_admin.xml?token=' . $self -> {token} . '&domain=' . $domain
345             . '&login=' . $login,
346             {
347             name => '/action/domain/name/text()',
348             new_admin => '/action/domain/new-admin/text()',
349             }
350             );
351             }
352            
353             sub domain_del_admin {
354             my $self = shift;
355             my $domain = shift;
356             my $login = shift;
357            
358             return $self -> __simple_query(
359             API_URL . 'api/multiadmin/del_admin.xml?token=' . $self -> {token} . '&domain=' . $domain
360             . '&login=' . $login,
361             {
362             name => '/action/domain/name/text()',
363             deleted => '/action/domain/new-admin/text()',
364             }
365             );
366             }
367            
368             sub domain_get_admins {
369             my $self = shift;
370             my $domain = shift;
371            
372             return $self -> __simple_query(
373             API_URL . 'api/multiadmin/get_admins.xml?token=' . $self -> {token} . '&domain=' . $domain,
374             {
375             name => '/action/domain/name/text()',
376             other_admins => '/action/domain/other-admins/login/',
377             }
378             );
379             }
380            
381             # MAILLISTS
382            
383             sub maillist_create {
384             my ($self, $domain, $listname) = @_;
385            
386             # NOTE: new user $listname will be created
387            
388             return $self -> __simple_query(
389             API_URL . 'api/create_general_maillist.xml?token=' . $self -> {token} . '&domain=' . $domain
390             . '&ml_name=' . $listname,
391             {
392             name => '/action/domains/domain/name/text()',
393             }
394             );
395             }
396            
397             sub maillist_destroy {
398             my ($self, $domain, $listname) = @_;
399            
400             return $self -> __simple_query(
401             API_URL . 'api/delete_general_maillist.xml?token=' . $self -> {token} . '&domain=' . $domain
402             . '&ml_name=' . $listname,
403             {
404             name => '/action/domains/domain/name/text()',
405             }
406             );
407             }
408            
409             # USERS
410            
411             sub create_user
412             {
413             my $self = shift;
414             my $login = shift;
415             my $pass = shift;
416             my $encr = shift;
417            
418             my $url;
419            
420             if ($encr)
421             {
422             $url = API_URL . 'reg_user_crypto.xml?token=' . $self -> {token} . '&login=' . $login
423             . '&password=' . $pass;
424             }
425             else
426             {
427             $url = API_URL . 'reg_user_token.xml?token=' . $self -> {token} . '&u_login=' . $login
428             . '&u_password=' . $pass;
429             }
430            
431             return undef unless $self -> __make_request($url);
432            
433             if ( my $uid = $self -> __get_node_text('/page/ok/@uid') )
434             {
435             return $uid;
436             }
437            
438             return $self -> __unknown_error();
439             }
440            
441             sub is_user_exists
442             {
443             my $self = shift;
444             my $login = shift;
445            
446             my $url = API_URL . 'check_user.xml?token=' . $self -> {token} . '&login=' . $login;
447            
448             return undef unless $self -> __make_request($url);
449            
450             if ( my $result = $self -> __get_node_text('/page/result/text()') )
451             {
452             return 1 if ( 'exists' eq $result );
453             return 0 if ( 'nouser' eq $result );
454             }
455            
456             return $self -> __unknown_error();
457             }
458            
459             sub update_user
460             {
461             my $self = shift;
462             my $login = shift;
463             my %data = @_;
464            
465             my $url = API_URL . '/edit_user.xml?token=' . $self -> {token} . '&login=' . $login
466             . '&password=' . $data{password} || ''
467             . '&iname=' . $data{iname} || ''
468             . '&fname=' . $data{fname} || ''
469             . '&sex=' . $data{sex} || ''
470             . '&hintq=' . $data{hintq} || ''
471             . '&hinta=' . $data{hinta} || '';
472            
473            
474             return undef unless $self -> __make_request($url);
475            
476             if ( my $uid = $self -> __get_node_text('/page/ok/@uid') )
477             {
478             return $uid;
479             }
480            
481             return $self -> __unknown_error();
482             }
483            
484             sub import_user
485             {
486             my $self = shift;
487             my $login = shift;
488             my $password = shift;
489             my %data = @_;
490            
491             $data{save_copy} = ($data{save_copy} and $data{save_copy} ne 'no') ? '1' : '0';
492            
493             my $url = API_URL . 'reg_and_imp.xml?token=' . $self -> {token}
494             . '&login=' . $login
495             . '&inn_password=' . $password
496             . '&ext_login=' . ( $data{ext_login} || $login )
497             . '&ext_password=' . $data{ext_password}
498             . '&fwd_email=' . ( $data{forward_to} || '' )
499             . '&fwd_copy=' . $data{save_copy};
500            
501             return undef unless $self -> __make_request($url);
502            
503             if ( $self -> __get_nodelist('/page/ok') -> [0] )
504             {
505             return 1;
506             }
507            
508             return $self -> __unknown_error();
509             }
510            
511             sub delete_user
512             {
513             my $self = shift;
514             my $login = shift;
515             my $domain = shift;
516            
517             my $url;
518            
519             if (defined $domain) {
520             $url = API_URL . 'del_user.xml?token=' . $self -> {token} . '&login=' . $login . '&domain=' . $domain;
521             } else {
522             $url = API_URL . 'delete_user.xml?token=' . $self -> {token} . '&login=' . $login;
523             }
524            
525             return undef unless $self -> __make_request($url);
526            
527             if ( $self -> __get_nodelist('/page/ok') -> [0] )
528             {
529             return 1;
530             }
531            
532             return $self -> __unknown_error();
533             }
534            
535             sub get_forward_list {
536             my ($self, $login) = @_;
537            
538             return $self -> __simple_query(
539             API_URL . 'get_forward_list.xml?token=' . $self -> {token} . '&login=' . $login,
540             {
541             filter_id => '/page/ok/filters/filter/id',
542             enabled => '/page/ok/filters/filter/enabled', # 'yes' / 'no'
543             forward => '/page/ok/filters/filter/forward', # 'yes' / 'no'
544             copy => '/page/ok/filters/filter/copy', # 'yes' / 'no'
545             to_address => '/page/ok/filters/filter/filter_param',
546             }
547             );
548             }
549            
550             sub delete_forward {
551             my ($self, $login, $filter_id) = @_;
552            
553             return $self -> __simple_query(
554             API_URL . 'delete_forward.xml?token=' . $self -> {token} . '&login=' . $login
555             . '&filter_id=' . $filter_id
556             );
557             }
558            
559             sub set_forward
560             {
561             my $self = shift;
562             my $login = shift;
563             my $address = shift;
564             my $save_copy = shift;
565            
566             $save_copy = ($save_copy and $save_copy ne 'no') ? 'yes' : 'no';
567            
568             my $url = API_URL . 'set_forward.xml?token=' . $self -> {token} . '&login=' . $login
569             . '&address=' . $address
570             . '©=' . $save_copy;
571            
572             return undef unless $self -> __make_request($url);
573            
574             if ( $self -> __get_nodelist('/page/ok') -> [0] )
575             {
576             return 1;
577             }
578            
579             return $self -> __unknown_error();
580             }
581            
582             sub get_user
583             {
584             my $self = shift;
585             my $login = shift;
586            
587             return $self -> __simple_query(
588             API_URL . 'get_user_info.xml?token=' . $self -> {token} . '&login=' . $login,
589             {
590             login => '/page/domain/user/login/text()',
591             domain => '/page/domain/name/text()',
592             birth_date => '/page/domain/user/birth_date/text()',
593             fname => '/page/domain/user/fname/text()',
594             iname => '/page/domain/user/iname/text()',
595             hinta => '/page/domain/user/hinta/text()',
596             hintq => '/page/domain/user/hintq/text()',
597             mail_format => '/page/domain/user/mail_format/text()',
598             charset => '/page/domain/user/charset/text()',
599             nickname => '/page/domain/user/nickname/text()',
600             sex => '/page/domain/user/sex/text()',
601             enabled => '/page/domain/user/enabled/text()',
602             signed_eula => '/page/domain/user/signed_eula/text()',
603             }
604             );
605             }
606            
607             sub get_unread_count
608             {
609             my $self = shift;
610             my $login = shift;
611            
612             my $url = API_URL . 'get_mail_info.xml?token=' . $self -> {token} . '&login=' . $login;
613            
614             return undef unless $self -> __make_request($url);
615            
616             my $count = $self -> __get_node_text('/page/ok/@new_messages');
617            
618             if ( defined $count )
619             {
620             return $count;
621             }
622            
623             return $self -> __unknown_error();
624             }
625            
626             sub get_user_list
627             {
628             my $self = shift;
629             my $page = shift || 1;
630             my $per_page = shift || 100;
631            
632             my $url = API_URL . 'get_domain_users.xml?token=' . $self -> {token}
633             . '&page= ' . $page # HACK XXX
634             . '&per_page=' . $per_page;
635             return undef unless $self -> __make_request($url);
636            
637             my @emails = ();
638            
639             for ( $self -> __get_nodelist('/page/domains/domain/emails/email/name') )
640             {
641             push( @emails, $_ -> textContent );
642             }
643            
644             $self -> {info} = {
645             'action-status' => $self -> __get_node_text('/page/domains/domain/emails/action-status/text()'),
646             'found' => $self -> __get_node_text('/page/domains/domain/emails/found/text()'),
647             'total' => $self -> __get_node_text('/page/domains/domain/emails/total/text()'),
648             'domain' => $self -> __get_node_text('/page/domains/domain/name/text()'),
649             'status' => $self -> __get_node_text('/page/domains/domain/status/text()'),
650             'emails-max-count' => $self -> __get_node_text('/page/domains/domain/emails-max-count/text()'),
651             'emails' => \@emails,
652             };
653            
654             return $self -> {info};
655             }
656            
657             sub prepare_import
658             {
659             my $self = shift;
660             my $server = shift;
661             my %data = @_;
662            
663             unless ($data{method} or $data{method} !~ /^pop3|imap$/i)
664             {
665             $data{method} = 'pop3';
666             }
667            
668             my $url = API_URL . 'set_domain.xml?token=' . $self -> {token}
669             . '&ext_serv=' . $server
670             . '&method=' . $data{method}
671             . '&callback=' . $data{callback};
672            
673             $url .= '&ext_port=' . $data{port} if $data{port};
674            
675             $url .= '&isssl=no' unless $data{use_ssl};
676            
677             return undef unless $self -> __make_request($url);
678            
679             if ( $self -> __get_nodelist('/page/ok') -> [0] )
680             {
681             return 1;
682             }
683            
684             return $self -> __unknown_error();
685             }
686            
687             sub start_import
688             {
689             my $self = shift;
690             my $login = shift;
691             my %data = @_;
692            
693             my $url = API_URL . 'start_import.xml?token=' . $self -> {token}
694             . '&login=' . $login
695             . '&ext_login=' . ($data{ext_login} || $login)
696             . '&password=' . $data{password};
697            
698             return undef unless $self -> __make_request($url);
699            
700             if ( $self -> __get_nodelist('/page/ok') -> [0] )
701             {
702             return 1;
703             }
704            
705             return $self -> __unknown_error();
706             }
707            
708             sub get_import_status
709             {
710             my $self = shift;
711             my $login = shift;
712            
713             my $url = API_URL . 'check_import.xml?token=' . $self -> {token} . '&login=' . $login;
714            
715             return undef unless $self -> __make_request($url);
716            
717             my $data =
718             {
719             last_check => $self -> __get_node_text('/page/ok/@last_check'),
720             imported => $self -> __get_node_text('/page/ok/@imported'),
721             state => $self -> __get_node_text('/page/ok/@state'),
722             };
723            
724             return $data;
725             }
726            
727             sub stop_import
728             {
729             my $self = shift;
730             my $login = shift;
731            
732             return undef unless ($login);
733            
734             my $url = API_URL . 'stop_import.xml?token=' . $self -> {token} . '&login=' . $login;
735            
736             return undef unless $self -> __make_request($url);
737            
738             if ( $self -> __get_nodelist('/page/ok') -> [0] )
739             {
740             return 1;
741             }
742            
743             return $self -> __unknown_error();
744             }
745            
746             sub import_imap_folder
747             {
748             my $self = shift;
749             my $login = shift;
750             my %data = shift;
751            
752             my $url = API_URL . 'import_imap.xml?token=' . $self -> {token}
753             . '&login=' . $login
754             . '&ext_password=' . $data{ext_password};
755            
756             $url .= '&ext_login=' . $data{ext_login} if (exists $data{ext_login});
757             $url .= '&int_password=' . $data{password} if (exists $data{password});
758             $url .= '©_one_folder=' . uri_escape_utf8($data{copy_one_folder}) if (exists $data{copy_one_folder});
759            
760             return undef unless $self -> __make_request($url);
761            
762             if ( $self -> __get_nodelist('/page/ok') -> [0] )
763             {
764             return 1;
765             }
766            
767             return $self -> __unknown_error();
768             }
769            
770             =encoding utf8
771            
772             =head1 NAME
773            
774             WWW::Yandex::PDD - Perl extension for Yandex mailhosting
775            
776            
777             =head1 SYNOPSIS
778            
779             Obtain token at L
780            
781             use WWW::Yandex::PDD;
782            
783             my $pdd = WWW::Yandex::PDD->new( token => 'abcdefghijklmnopqrstuvwxyz01234567890abcdefghijklmnopqrs' );
784             $pdd->create_user( 'mynewuser', 'mysecretpassword' );
785            
786            
787             =head1 DESCRIPTION
788            
789             L allows to manage user mail accounts on Yandex mailhosting
790            
791            
792             =head1 METHODS
793            
794             =over
795            
796             =item $pdd->new( token => $token );
797            
798             =item $pdd->new( token => $token, cert_file => $cert_file );
799            
800             Construct a new L object
801            
802             $token A string obtained at L
803            
804             $cert_file New $ENV{HTTPS_CA_FILE} value
805            
806            
807             =item $pdd->get_last_error()
808            
809             Returns undef if there was no error; otherwise
810            
811             $return = {
812             code => $error -> {code},
813             info => $error -> {info},
814             };
815            
816            
817             =back
818            
819            
820             =head2 DOMAINS
821            
822             =over 2
823            
824             =item $pdd->domain_reg( $domain )
825            
826             Sign up $domain for Yandex Mail API
827            
828             Returns undef if error, otherwise
829            
830             $return = {
831             name => $domain_name,
832             secret_name => $secret_name,
833             secret_value => $secret_value,
834             }
835            
836            
837             =item $pdd->domain_unreg( $domain )
838            
839             Disconnect this $domain
840            
841             Returns 1 if success, undef if error
842            
843            
844             =item $pdd->domain_add_logo( $domain, $file_name )
845            
846             Adds logo from $file_name (jpg, gif, png; < 2 Mb) to $domain.
847            
848             Returns undef if error, otherwise
849            
850             $return = {
851             name => $domain_name,
852             logo_url => $logo_url,
853             };
854            
855            
856             =item $pdd->domain_del_logo( $domain )
857            
858             Removes logo from $domain
859            
860             Returns undef if error, otherwise
861            
862             $return = {
863             name => $domain_name,
864             }
865            
866            
867             =item $pdd->domain_add_admin( $domain, $login )
868            
869             Adds new administrator $login for domain $domain.
870            
871             Note: $login should be a separate mail box hosted on yandex.ru outside of $domain. For example,
872             if you are adding foobar@yandex.ru, $login is 'foobar'
873            
874             Returns undef if error, otherwise
875            
876             $return = {
877             name => 'somedomain.org',
878             new_admin => 'foobar',
879             }
880            
881            
882             =item $pdd->domain_del_admin( $domain, $login )
883            
884             Removes $login from domain $domain administrators.
885            
886             Returns undef if error, otherwise
887            
888             $return = {
889             name => 'somedomain.org',
890             deleted => 'foobar',
891             }
892            
893            
894             =item $pdd->domain_get_admins( $domain )
895            
896             Returns a list of secondary $domain administrators.
897            
898             Returns undef if error, otherwise
899            
900             $return = {
901             name => 'somedomain.org',
902             other_admins => [ 'admin', 'anotheradmin' ],
903             }
904            
905            
906             =item $pdd->domain_set_default_user( $domain, $login )
907            
908             Sets address C<$login>@C<$domain> as a default address. All mail to non-existing addresses
909             will route to this poor guy.
910            
911             Returns undef if error, otherwise
912            
913             $return = {
914             name => 'somedomain.org',
915             email => 'johndoe',
916             }
917            
918            
919             =back
920            
921            
922             =head2 USERS
923            
924             =over 2
925            
926             =item $pdd->create_user( $login, $password );
927            
928             =item $pdd->create_user( $login, $encrypted_password, 'encrypted' );
929            
930             $encrypted_password is MD5-CRYPT password hash: "$1$" + 8 character salt [a-zA-Z0-9./] + "$" + 22 character checksum [a-zA-Z0-9./]
931            
932            
933             =item $pdd->update_user( $login, password => $password, iname => $iname, fname => $fname, sex => $sex, hintq => $hintq, hinta => $hinta )
934            
935             See L<$pdd->get_user> for parameters meaning
936            
937             Returns UID if success, undef otherwise
938            
939            
940             =item $pdd->delete_user( $login )
941            
942             =item $pdd->delete_user( $login, $domain )
943            
944             Optional $domain if $login is in another domain
945            
946             Returns 1 if success
947            
948            
949             =item $pdd->get_unread_count($login)
950            
951             Returns number of unread messages, undef if error
952            
953            
954             =item $pdd->get_user( $login )
955            
956             Returns undef if fail, or the following structure if success:
957            
958             $result = {
959             domain => 'mydomain.org',
960             login => 'username',
961             birth_date => '1900-01-01',
962             iname => 'John',
963             fname => 'Doe',
964             hintq => 'Your mother\'s maiden name?', # utf-8
965             hinta => '*****',
966             mail_format => '', # preferred mail format
967             charset => '', # preferred charset
968             nickname => 'johnny',
969             sex => 1, # 0 - N/A, 1 - male, 2 - female
970             enabled => 1, # 1 - normal, mail accepted; 0 - locked, mail rejected
971             signed_eula => 1, # user accepted EULA: 1 - yes, 0 - no
972             };
973            
974            
975             =item $pdd->get_user_list($page, $per_page)
976            
977             Returns domain information and user list, undef if error
978            
979             $page Page number.
980            
981             $per_page Number of mailbox records on a single page; cannot be more than 100; 100 records by default.
982            
983             $result = {
984             'action-status' => '', # error message
985             'found' => 18, # number of users returned
986             'total' => 18, # total number of users in this domain
987             'domain' => 'mydomain.org',
988             'status' => 'added', # added, mx-activate, domain-activate
989             'emails-max-count' => 1000, # maximum users for this domain
990             'emails' => [ 'jdoe', 'mkay' ], # user list
991             }
992            
993            
994             =item $pdd->is_user_exists( $login )
995            
996             Returns 1 if exists, 0 if doesn't exist, undef if error
997            
998            
999             =item $pdd->set_forward( $login, $forward_to, $save_copy )
1000            
1001             Sets forwarding to $forward_to
1002            
1003             $save_copy: "yes", "no"
1004            
1005             Returns 1 if OK, undef if error
1006            
1007            
1008             =item $pdd->get_forward_list($login)
1009            
1010             Returns undef if error
1011            
1012             Returns full description of forward rules for $login:
1013            
1014             $result = {
1015             filter_id => 12342343,
1016             enabled => 'yes', # 'yes' / 'no'
1017             forward => 'yes', # 'yes' / 'no'
1018             copy => 'no', # 'yes' / 'no'
1019             to_address => 'sameuser@otherdomain.org',
1020             }
1021            
1022            
1023             =item $pdd->delete_forward( $login, $filter_id )
1024            
1025             Removes forward for user $login, forward rule $filter_id. Returns undef if error.
1026            
1027            
1028             =back
1029            
1030            
1031             =head2 IMPORT
1032            
1033             =over 2
1034            
1035             =item $pdd->prepare_import( $server, method => $method, port => $port, callback = $callback, use_ssl => $use_ssl )
1036            
1037             Set import options for the domain
1038            
1039             $method: 'pop3', 'imap', default 'pop3'
1040            
1041             $port: 100 POP3 w/o SSL, 995 POP3 with SSL; optional
1042            
1043             $use_ssl: 'yes'/'no'; default 'no'
1044            
1045             $callback: URL. If not empty, an HTTP request will be made to this address
1046             with login="imported user's login" parameter after finishing import
1047            
1048             Returns 1 if OK, undef if error
1049            
1050            
1051             =item $pdd->import_user( $login, $password, ext_login => $ext_login, ext_password => $ext_password, forward_to => $forward, save_copy => $save_copy )
1052            
1053             Register a new user and import all the mail from another server
1054            
1055             $ext_login login on the source server, defaults to $login
1056             $ext_password user's password on the source server, defaults to $password
1057             $forward_to optional, set forwarding for this new mailbox
1058             $save_copy works only if forwarding is on; 0 - do not save copies in the local mailbox, 1 - save copies and forward
1059            
1060            
1061             =item $pdd->get_import_status( $login )
1062            
1063             Returns: {
1064             last_check => $last_check,
1065             imported => $imported,
1066             state => $state
1067             };
1068            
1069            
1070             =item $pdd->start_import( $login, ext_login => $ext_login, password => $password )
1071            
1072             $ext_login login on the source, defaults to $login
1073             $password on the source
1074            
1075             Returns 1 if OK, undef if error
1076            
1077            
1078             =item $pdd->stop_import($login)
1079            
1080             Returns 1 if OK, undef if error
1081            
1082             =item $pdd->import_imap_folder($login, password => $password, ext_login => $ext_login, ext_password => $ext_password, copy_one_folder = $copy_one_folder)
1083            
1084             $ext_login login on the source, defaults to $login
1085             $ext_password password on the source
1086             $password in the domain, mandatory if $login is a new user
1087             $copy_one_folder folder on the source; UTF-8, optional
1088            
1089             Returns 1 if OK, undef if error
1090            
1091             =back
1092            
1093            
1094             =head2 MAILLISTS
1095            
1096             =over 2
1097            
1098             =item $pdd->maillist_create( $domain, $login, $listname )
1099            
1100             Creates a new mailbox $login. Messages to this mailbox will be sent to all $domain users
1101            
1102             Returns undef if error, otherwise
1103            
1104             $return = {
1105             name => 'somedomain.org',
1106             }
1107            
1108            
1109            
1110             =item $pdd->maillist_destroy( $domain, $login, $listname )
1111            
1112             Deletes previously created list and mailbox $login
1113            
1114             Returns undef if error, otherwise
1115            
1116             $return = {
1117             name => 'somedomain.org',
1118             }
1119            
1120            
1121             =back
1122            
1123            
1124             =head1 SEE ALSO
1125            
1126             L
1127             L
1128             L
1129            
1130            
1131             =head1 ENVIRONMENT
1132            
1133             Setting C environment variable to some debug file name causes L to turn on internal
1134             debugging, and put in this file server XML responses.
1135            
1136            
1137             =head1 AUTHORS
1138            
1139             dctabuyz,
1140             Andrei Lukovenko,
1141            
1142            
1143             =head1 BUGS
1144            
1145             Please report any bugs or feature requests to C, or through
1146             the web interface at L. I will be notified, and then you'll
1147             automatically be notified of progress on your bug as I make changes.
1148            
1149            
1150             =head1 HISTORY
1151            
1152             Original version by dctabuyz: L
1153            
1154            
1155             =head1 SUPPORT
1156            
1157             You can find documentation for this module with the perldoc command.
1158            
1159             perldoc WWW::Yandex::PDD
1160            
1161            
1162             You can also look for information at:
1163            
1164             =over 4
1165            
1166             =item * RT: CPAN's request tracker (report bugs here)
1167            
1168             L
1169            
1170             =item * AnnoCPAN: Annotated CPAN documentation
1171            
1172             L
1173            
1174             =item * CPAN Ratings
1175            
1176             L
1177            
1178             =item * Search CPAN
1179            
1180             L
1181            
1182             =back
1183            
1184            
1185             =head1 COPYRIGHT AND LICENSE
1186            
1187             Copyright (c) 2010
1188             Copyright (c) 2013
1189            
1190             This library is free software; you can redistribute it and/or modify
1191             it under the same terms as Perl itself, either Perl version 5.8.7 or,
1192             at your option, any later version of Perl 5 you may have available.
1193            
1194             =cut
1195            
1196             1;