File Coverage

lib/Mediafire/Api/UploadFile.pm
Criterion Covered Total %
statement 44 65 67.6
branch 0 6 0.0
condition 0 14 0.0
subroutine 15 17 88.2
pod 0 2 0.0
total 59 104 56.7


line stmt bran cond sub pod time code
1             package Mediafire::Api::UploadFile;
2              
3 1     1   19 use 5.008001;
  1         4  
4 1     1   7 use utf8;
  1         1  
  1         11  
5 1     1   40 use strict;
  1         2  
  1         19  
6 1     1   4 use warnings;
  1         18  
  1         34  
7 1     1   20 use open qw(:std :utf8);
  1         3  
  1         6  
8 1     1   150 use Carp qw/croak carp/;
  1         2  
  1         59  
9 1     1   6 use URI::Escape;
  1         2  
  1         60  
10 1     1   6 use LWP::UserAgent;
  1         1  
  1         18  
11 1     1   4 use File::Basename;
  1         1  
  1         58  
12 1     1   6 use HTTP::Request;
  1         2  
  1         18  
13 1     1   4 use JSON::XS;
  1         1  
  1         55  
14 1     1   477 use MIME::Detect;
  1         106375  
  1         42  
15 1     1   532 use Crypt::Digest::SHA256 qw/sha256_hex/;
  1         4660  
  1         81  
16 1     1   501 use Time::HiRes qw/gettimeofday/;
  1         1333  
  1         4  
17              
18 1     1   631 use Mediafire::Api::File;
  1         3  
  1         2220  
19              
20             our $VERSION = '0.01';
21              
22             my $DEFAULT_BUFF_SIZE = 1048576;
23              
24             ############################ PRIVATE METHODS ############################################
25             my ($getSha256Sum, $checkUploadFile, $getFileFromCache, $checkResumeUpload, $getMimeType, $uploadF);
26              
27             $getSha256Sum = sub {
28             my ($fname) = @_;
29             my $sha = Crypt::Digest::SHA256->new();
30             $sha->addfile($fname);
31             return $sha->hexdigest;
32             };
33              
34             $checkUploadFile = sub {
35             my ($self) = @_;
36              
37             my $url = 'https://www.mediafire.com/api/1.5/upload/check.php';
38              
39             my @sec = gettimeofday();
40             my $microseconds = substr(join('', @sec), 0, 13);
41              
42             my %param = (
43             'hash' => $self->{file}->hash,
44             'size' => $self->{file}->size,
45             'filename' => $self->{file}->name,
46             'unit_size' => $self->{buff_size},
47             'resumable' => 'yes',
48             'preemptive' => 'yes',
49             'folder_key' => $self->{path},
50             'session_token' => $self->{session_token},
51             'response_format' => 'json',
52             $microseconds => '',
53             );
54              
55             my $param_str = join('&', map {"$_=" . uri_escape($param{$_})} keys %param);
56             my $full_url = $url . '?' . $param_str;
57             my $res = $self->{ua}->get($full_url);
58             my $code = $res->code;
59             if ($code ne '200') {
60             croak "Wrong response code checkUploadFile(). Url: '$full_url'. Code: $code";
61             }
62             my $json_res = eval {
63             decode_json($res->decoded_content);
64             };
65             if ($@) {
66             croak "Can't parse respone '" . $res->decoded_content . "' to json";
67             }
68              
69             # Get json response
70             my $response = $json_res->{response};
71             if ($response->{result} ne 'Success') {
72             croak "checkUploadFile() not success";
73             }
74              
75             # Limit exceeded
76             if ($response->{storage_limit_exceeded} ne 'no') {
77             croak "Can't checkUploadFile. Storage limit exceeded";
78             }
79              
80             my $file_key = $response->{preemptive_quickkey} // $response->{duplicate_quickkey};
81             $self->{file}->key($file_key);
82             $self->{upload_url} = $response->{upload_url}->{resumable};
83             return $response;
84             };
85              
86             $getFileFromCache = sub {
87             my ($self) = @_;
88              
89             my $url = 'https://www.mediafire.com/api/1.5/upload/instant.php';
90              
91             my @sec = gettimeofday();
92             my $microseconds = substr(join('', @sec), 0, 13);
93              
94             my %param = (
95             'hash' => $self->{file}->hash,
96             'size' => $self->{file}->size,
97             'filename' => $self->{file}->name,
98             'folder_key' => $self->{path},
99             'session_token' => $self->{session_token},
100             'response_format' => 'json',
101             $microseconds => '',
102             );
103              
104             my $param_str = join('&', map {"$_=" . uri_escape($param{$_})} keys %param);
105             my $full_url = $url . '?' . $param_str;
106             my $res = $self->{ua}->get($full_url);
107             my $code = $res->code;
108             if ($code ne '200') {
109             croak "Wrong response code checkUploadFile(). Url: '$full_url'. Code: $code";
110             }
111             my $json_res = eval {
112             decode_json($res->decoded_content);
113             };
114             if ($@) {
115             croak "Can't parse respone '" . $res->decoded_content . "' to json";
116             }
117              
118             # Get json response
119             my $response = $json_res->{response};
120             if ($response->{result} ne 'Success') {
121             croak "getFileFromCache() not success";
122             }
123             my $file_key = $response->{quickkey};
124             $self->{file}->key($file_key);
125              
126             return 1;
127             };
128              
129             $checkResumeUpload = sub {
130             my ($self) = @_;
131              
132             my $ua = $self->{ua};
133             my $url = 'https://ul.mediafireuserupload.com/api/1.5/upload/resumable.php';
134             my %param = (
135             'session_token' => $self->{session_token},
136             'uploadkey' => $self->{path},
137             'response_format' => 'json',
138             );
139              
140             my $headers = [
141             'Access-Control-Request-Method' => 'POST',
142             'Origin' => 'https://www.mediafire.com',
143             'Access-Control-Request-Headers' => 'content-type,x-filehash,x-filename,x-filesize,x-filetype,x-unit-hash,x-unit-id,x-unit-size',
144             'Accept' => '*/*',
145             'Accept-Encoding' => 'gzip, deflate, br',
146             'Accept-Language' => 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
147             ];
148             my $param_str = join('&', map {"$_=" . uri_escape($param{$_})} keys %param);
149             my $full_url = $url . '?' . $param_str;
150             my $request = HTTP::Request->new('OPTIONS', $full_url, $headers);
151             my $res = $ua->request($request);
152             my $code = $res->code;
153             if ($code ne '200') {
154             croak "Wrong response code on url: '$full_url'. Code: $code";
155             }
156              
157             return 1;
158              
159             };
160              
161             # Upload file
162             $uploadF = sub {
163             my ($self) = @_;
164              
165             my $upload_file = $self->{upload_file};
166              
167             my %param = (
168             'session_token' => $self->{session_token},
169             'uploadkey' => $self->{path},
170             'response_format' => 'json',
171             );
172             my $param_str = join('&', map {"$_=" . uri_escape($param{$_})} keys %param);
173             my $url = $self->{upload_url} . '?' . $param_str;
174              
175             my $unit_id = 0;
176             my $filebuf;
177             open my $FH, "<$upload_file" or croak "Can't open $upload_file $!";
178             binmode $FH;
179              
180             my $json_res;
181             while (my $bytes = read($FH, $filebuf, $self->{buff_size})) {
182             my $unit_hash = sha256_hex($filebuf);
183             my @headers = (
184             "Accept" => "*/*",
185             "Accept-Language" => "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
186             "Accept-Encoding" => "gzip, deflate, br",
187             "Content-Type" => "application/octet-stream",
188             "Referer" => "https://www.mediafire.com/uploads",
189             "Origin" => "https://www.mediafire.com",
190             "X-Filesize" => $self->{file}->size,
191             "X-Filename" => $self->{file}->name,
192             "X-Filetype" => $getMimeType->($self->{file}->name),
193             "X-Filehash" => $self->{file}->hash,
194             "X-Unit-Hash" => $unit_hash,
195             "X-Unit-Size" => $bytes,
196             "X-Unit-Id" => $unit_id,
197             "Content" => $filebuf,
198             );
199             my $res = $self->{ua}->post($url, @headers);
200             my $code = $res->code;
201             if ($code ne '200') {
202             croak "Wrong response code on request to url '$url'. Code: '$code'";
203             }
204              
205             $json_res = eval {
206             decode_json($res->decoded_content);
207             };
208             if ($@) {
209             croak "Can't decode response to json. Response: '" . $res->decoded_content . "'";
210             }
211              
212             if ($json_res->{response}->{result} ne 'Success') {
213             croak "Response on url '$url' not success";
214             }
215              
216             # Check all units ready
217             if ($json_res->{response}->{resumable_upload}->{all_units_ready} eq 'yes') {
218             last;
219             }
220              
221             ++$unit_id;
222             }
223             close $FH;
224              
225             # Check all units ready
226             if ($json_res->{response}->{resumable_upload}->{all_units_ready} ne 'yes') {
227             croak "Not all parts of file '$upload_file' uploaded. Wrong answer from server";
228             }
229              
230             return 1;
231             };
232              
233             $getMimeType = sub {
234             my ($fname) = @_;
235             my $default_mime = 'application/zip';
236             my $mime = MIME::Detect->new();
237             my @types = $mime->mime_types_from_name($fname);
238             if (@types) {
239             return $types[0]->mime_type;
240             }
241             return $default_mime;
242             };
243              
244             ########################################################################################
245              
246              
247             sub new {
248 0     0 0   my ($class, %opt) = @_;
249 0           my $self = {};
250 0   0       $self->{ua} = $opt{-ua} // croak "You must specify param '-ua' for method new";
251 0   0       $self->{session_token} = $opt{-session_token} // croak "You must specify '-session_token' param";
252 0   0       $self->{buff_size} = $opt{-buff_size} // $DEFAULT_BUFF_SIZE;
253 0           bless $self, $class;
254 0           return $self;
255             }
256              
257             sub uploadFile {
258 0     0 0   my ($self, %opt) = @_;
259              
260 0   0       $self->{upload_file} = $opt{'-file'} || croak "You must specify -file param for method uploadFile";
261 0   0       $self->{path} = $opt{'-path'} || 'myfiles';
262              
263              
264 0 0         if (not -f $self->{upload_file}) {
265 0           croak "File '" . $self->{upload_file} . "' not exists";
266             }
267              
268             $self->{file} = Mediafire::Api::File->new(
269             -size => -s $self->{upload_file},
270             -name => basename($self->{upload_file}),
271 0           -hash => $getSha256Sum->($self->{upload_file}),
272             );
273              
274              
275             # Get upload url
276 0           my $response = $self->$checkUploadFile();
277 0 0         if ($response->{hash_exists} eq 'yes') {
278             # No need upload file. Get file from cache
279 0           $self->$getFileFromCache();
280             }
281             else {
282             # Upload file
283 0           $self->$checkResumeUpload();
284 0           $self->$uploadF();
285             }
286              
287             # Check exists file key
288 0 0         if (not defined($self->{file}->key)) {
289 0           croak "Key of upload file '$self->{upload_file}' not exists. Error on upload file to server";
290             }
291              
292 0           return $self->{file};
293             }
294              
295              
296              
297              
298             1;