File Coverage

lib/Mailru/Cloud/Auth.pm
Criterion Covered Total %
statement 37 109 33.9
branch 0 46 0.0
condition 1 8 12.5
subroutine 11 16 68.7
pod 1 3 33.3
total 50 182 27.4


line stmt bran cond sub pod time code
1             package Mailru::Cloud::Auth;
2              
3 1     1   71515 use 5.008001;
  1         4  
4 1     1   6 use strict;
  1         2  
  1         32  
5 1     1   6 use warnings;
  1         2  
  1         37  
6 1     1   654 use utf8;
  1         14  
  1         6  
7 1     1   500 use open qw(:std :utf8);
  1         1208  
  1         5  
8 1     1   820 use LWP::UserAgent;
  1         46007  
  1         38  
9 1     1   9 use HTTP::Request;
  1         2  
  1         29  
10 1     1   749 use JSON::XS;
  1         5196  
  1         60  
11 1     1   8 use URI::Escape;
  1         2  
  1         57  
12 1     1   9 use Carp qw/carp croak/;
  1         2  
  1         1435  
13              
14             our $VERSION = '0.09';
15              
16             sub new {
17 1     1 0 730 my ($class, %opt) = @_;
18 1   50     11 my $max_redirect = $opt{'-max_redirect'} // 30;
19 1         2 my $self = {};
20 1         4 $self->{debug} = $opt{-debug};
21 1         9 my $ua = LWP::UserAgent->new (
22             agent => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36',
23             cookie_jar => {},
24             );
25 1         10586 $self->{ua} = $ua;
26 1         6 $self->{ua}->max_redirect($max_redirect);
27 1         26 return bless $self, $class;
28             }
29              
30             sub login {
31 0     0 1   my ($self, %opt) = @_;
32 0   0       $self->{login} = $opt{-login} || $self->{login} || croak "You must specify -login opt for 'login' method";
33 0   0       $self->{password} = $opt{-password} || $self->{password} || croak "You must specify -password opt for 'login' method";
34 0           my $ua = $self->{ua};
35 0           my $res;
36              
37             #Get login token
38 0           $res = $ua->get('https://mail.ru');
39 0 0         if ($res->code ne '200') {
40 0           croak "Can't get start mail.ru page. Code: " . $res->code;
41             }
42 0           my ($login_token) = $res->decoded_content =~ /CSRF\s*[:=]\s*"([0-9A-Za-z]+?)"/;
43 0 0         if (not $login_token) {
44 0           croak "Can't found login token";
45             }
46              
47             #Login
48             my %param = (
49             'login' => $self->{login},
50             'password' => $self->{password},
51 0           'saveauth' => 1,
52             'project' => 'e.mail.ru',
53             'token' => $login_token,
54             );
55 0           my %headers = (
56             'Content-type' => 'application/x-www-form-urlencoded',
57             'Accept' => '*/*',
58             'Accept-Encoding' => 'gzip, deflate, br',
59             'Accept-Language' => 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
60             'Referer' => 'https://mail.ru/?from=logout',
61             'Origin' => 'https://mail.ru',
62             );
63              
64 0           $res = $ua->post('https://auth.mail.ru/jsapi/auth', \%param, %headers);
65 0 0         if ($res->code ne '200') {
66 0           croak "Wrong response code from login form: " . $res->code;
67             }
68              
69 0           my $json = decode_json($res->decoded_content);
70 0 0         if ($json->{status} eq 'fail') {
71 0           croak "Fail login: $json->{code}";
72             }
73              
74 0 0         $self->__getToken() or return;
75 0           return $self->{authToken};
76             }
77              
78             sub __getToken {
79 0     0     my $self = shift;
80              
81 0           my %headers = (
82             'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
83             'Accept-Encoding' => 'gzip, deflate, br',
84             'Accept-Language' => 'ru,en-US;q=0.9,en;q=0.8',
85             'Referer' => 'https://mail.ru/',
86             'Sec-Fetch-Dest' => 'document',
87             'Sec-Fetch-Mode' => 'navigate',
88             'Sec-Fetch-Site' => 'same-site',
89             'Sec-Fetch-User' => '?1',
90             'Upgrade-Insecure-Requests' => '1',
91             );
92 0           my $res = $self->{ua}->get('https://auth.mail.ru/sdc?from=' . uri_escape('https://cloud.mail.ru/?from=promo&from=authpopup') , %headers);
93              
94 0 0         if ($res->is_success) {
95 0           my $content = $res->decoded_content;
96 0           $DB::single = 1;
97 0 0         if ($content =~ /"csrf"\s*:\s*"([a-zA-Z0-9]+?)"/) {
98 0           $self->{authToken} = $1;
99 0 0         carp "Found authToken: $self->{authToken}" if $self->{debug};
100              
101 0 0         if ($content =~ /"email"\s*:\s*"(.+?)"/) {
102 0           $self->{email} = $1;
103 0 0         carp "Found email: $self->{email}" if $self->{debug};
104              
105             #Get BUILD
106 0           $self->{build} = 'hotfix_CLOUDWEB-7726_50-0-3.201710311503';
107 0 0         if ($content =~ /"BUILD"\s*:\s*"(.+?)"/) {
108 0           $self->{build} = $1;
109 0 0         carp "Found and use new build $self->{build}" if $self->{debug};
110             }
111              
112             #Get x-page-id
113 0           $self->{'x-page-id'} = 'f9jfLFeHA5';
114 0 0         if ($content =~ /"x-page-id"\s*:\s*"(.+?)"/) {
115 0           $self->{'x-page-id'} = $1;
116 0 0         carp "Found and use new x-page_id $self->{build}" if $self->{debug};
117             }
118              
119             #Parse free space info
120 0           $self->{info} = __parseInfo(\$content);
121 0           return 1;
122             }
123              
124             }
125             }
126 0           return;
127             }
128              
129             sub info {
130 0     0 0   my $self = shift;
131 0 0         if ($self->{info}) {
132 0           my %info = map {$_, $self->{info}->{$_}} keys %{$self->{info}};
  0            
  0            
133 0           return \%info;
134             }
135 0           return;
136             }
137              
138             sub __parseInfo {
139 0     0     my $content = shift;
140 0           my %info = (
141             'used_space' => '',
142             'total_space' => '',
143             'file_size_limit' => '',
144             );
145              
146 0 0         if (my ($size_block) = $$content =~ /"space":\s*{([^}]*)}/s) {
147 0           while ($size_block =~ /"([^"]+)":\s*(\w+?)\b/gm) {
148 0 0         if ($1 eq 'bytes_total') {
    0          
149 0           $info{total_space} = $2;
150             }
151             elsif ($1 eq 'bytes_used') {
152 0           $info{used_space} = $2;
153             }
154             }
155             }
156              
157 0 0         if ($$content =~ /"file_size_limit":\s*(.+?)[,\s]/) {
158 0           $info{file_size_limit} = $1;
159             }
160 0           return \%info;
161             }
162              
163             sub __isLogin {
164 0     0     my $self = shift;
165 0 0         if ($self->{authToken}) {
166 0           my $ua = $self->{ua};
167 0           my $res = $ua->get('https://auth.mail.ru/cgi-bin/auth?mac=1&Login=' . uri_escape($self->{login}));
168 0           my $code = $res->code;
169 0 0         if ($code ne '200') {
170 0           croak "Can't get status about login";
171             }
172 0           my $json_res = decode_json($res->content);
173 0 0         $json_res->{status} eq 'ok' and return 1;
174 0 0         $self->login() and return 1;
175             }
176              
177 0           croak "Not logined";
178             }
179              
180             1;
181              
182              
183             __END__