line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::Google::Drive; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
2638
|
use 5.008001; |
|
1
|
|
|
|
|
4
|
|
4
|
1
|
|
|
1
|
|
8
|
use strict; |
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
36
|
|
5
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
26
|
|
6
|
1
|
|
|
1
|
|
4
|
use utf8; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
725
|
use LWP::UserAgent; |
|
1
|
|
|
|
|
48215
|
|
|
1
|
|
|
|
|
39
|
|
9
|
1
|
|
|
1
|
|
10
|
use HTTP::Request; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
26
|
|
10
|
1
|
|
|
1
|
|
791
|
use JSON::XS; |
|
1
|
|
|
|
|
5231
|
|
|
1
|
|
|
|
|
58
|
|
11
|
1
|
|
|
1
|
|
8
|
use URI; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
26
|
|
12
|
1
|
|
|
1
|
|
5
|
use File::Basename; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
67
|
|
13
|
|
|
|
|
|
|
|
14
|
1
|
|
|
1
|
|
10
|
use Carp qw/carp croak/; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
48
|
|
15
|
|
|
|
|
|
|
|
16
|
1
|
|
|
1
|
|
502
|
use Net::Google::OAuth; |
|
1
|
|
|
|
|
1502
|
|
|
1
|
|
|
|
|
2714
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our $DOWNLOAD_BUFF_SIZE = 1024; |
21
|
|
|
|
|
|
|
our $UPLOAD_BUFF_SIZE = 256 * 1024; |
22
|
|
|
|
|
|
|
our $FILE_API_URL = 'https://www.googleapis.com/drive/v3/files'; |
23
|
|
|
|
|
|
|
our $FILE_API2_URL = 'https://www.googleapis.com/drive/v2/files'; |
24
|
|
|
|
|
|
|
our $UPLOAD_FILE_API_URL = 'https://www.googleapis.com/upload/drive/v3/files'; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub new { |
27
|
1
|
|
|
1
|
1
|
163
|
my ($class, %opt) = @_; |
28
|
|
|
|
|
|
|
|
29
|
1
|
|
|
|
|
4
|
my $self = {}; |
30
|
1
|
|
33
|
|
|
6
|
my $client_id = $opt{-client_id} // croak "You must specify '-client_id' param"; |
31
|
1
|
|
33
|
|
|
4
|
my $client_secret = $opt{-client_secret} // croak "You must specify '-client_secret' param"; |
32
|
1
|
|
33
|
|
|
4
|
$self->{access_token} = $opt{-access_token} // croak "You must specify '-access_token' param"; |
33
|
1
|
|
33
|
|
|
15
|
$self->{refresh_token} = $opt{-refresh_token} // croak "You must specify '-refresh_token' param"; |
34
|
1
|
|
|
|
|
13
|
$self->{ua} = LWP::UserAgent->new(); |
35
|
|
|
|
|
|
|
|
36
|
1
|
|
|
|
|
2929
|
$self->{oauth} = Net::Google::OAuth->new( |
37
|
|
|
|
|
|
|
-client_id => $client_id, |
38
|
|
|
|
|
|
|
-client_secret => $client_secret, |
39
|
|
|
|
|
|
|
); |
40
|
1
|
|
|
|
|
240386
|
bless $self, $class; |
41
|
1
|
|
|
|
|
7
|
return $self; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub searchFileByName { |
45
|
2
|
|
|
2
|
1
|
132709
|
my ($self, %opt) = @_; |
46
|
2
|
|
33
|
|
|
11
|
my $filename = $opt{-filename} || croak "You must specify '-filename' param"; |
47
|
|
|
|
|
|
|
|
48
|
2
|
|
|
|
|
14
|
my $search_res = $self->__searchFile('name=\'' . $filename . "'"); |
49
|
|
|
|
|
|
|
|
50
|
2
|
|
|
|
|
12
|
return $search_res; |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub searchFileByNameContains { |
54
|
1
|
|
|
1
|
1
|
526
|
my ($self, %opt) = @_; |
55
|
1
|
|
33
|
|
|
8
|
my $filename = $opt{-filename} || croak "You must specify '-filename' param"; |
56
|
|
|
|
|
|
|
|
57
|
1
|
|
|
|
|
6
|
my $search_res = $self->__searchFile('name contains \'' . $filename . "'"); |
58
|
|
|
|
|
|
|
|
59
|
1
|
|
|
|
|
8
|
return $search_res; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub downloadFile { |
64
|
1
|
|
|
1
|
1
|
1211
|
my ($self, %opt) = @_; |
65
|
1
|
|
33
|
|
|
5
|
my $file_id = $opt{-file_id} || croak "You must specify '-file_id' param"; |
66
|
1
|
|
33
|
|
|
6
|
my $dest_file = $opt{-dest_file} || croak "You must specify '-dest_file' param"; |
67
|
1
|
|
|
|
|
3
|
my $ua = $self->{ua}; |
68
|
1
|
|
|
|
|
5
|
my $access_token = $self->__getAccessToken(); |
69
|
|
|
|
|
|
|
|
70
|
1
|
|
|
|
|
13
|
my $uri = URI->new(join('/', $FILE_API_URL, $file_id)); |
71
|
1
|
|
|
|
|
119
|
$uri->query_form( |
72
|
|
|
|
|
|
|
'alt' => 'media', |
73
|
|
|
|
|
|
|
); |
74
|
1
|
|
|
|
|
95
|
my $headers = [ |
75
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
76
|
|
|
|
|
|
|
]; |
77
|
|
|
|
|
|
|
|
78
|
1
|
|
|
|
|
8
|
my $request = HTTP::Request->new( 'GET', |
79
|
|
|
|
|
|
|
$uri, |
80
|
|
|
|
|
|
|
$headers, |
81
|
|
|
|
|
|
|
); |
82
|
|
|
|
|
|
|
|
83
|
1
|
|
|
|
|
152
|
my $FL; |
84
|
|
|
|
|
|
|
my $response = $ua->request($request, sub { |
85
|
3
|
100
|
|
3
|
|
727858
|
if (not $FL) { |
86
|
1
|
50
|
|
|
|
170
|
open $FL, ">$dest_file" or croak "Cant open $dest_file to write $!"; |
87
|
1
|
|
|
|
|
8
|
binmode $FL; |
88
|
|
|
|
|
|
|
} |
89
|
3
|
|
|
|
|
13
|
print $FL $_[0]; |
90
|
|
|
|
|
|
|
}, |
91
|
1
|
|
|
|
|
9
|
$DOWNLOAD_BUFF_SIZE |
92
|
|
|
|
|
|
|
); |
93
|
1
|
50
|
|
|
|
2057
|
if ($FL) { |
94
|
1
|
|
|
|
|
62
|
close $FL; |
95
|
|
|
|
|
|
|
} |
96
|
1
|
|
|
|
|
8
|
my $response_code = $response->code(); |
97
|
1
|
50
|
|
|
|
16
|
if ($response_code != 200) { |
98
|
0
|
|
|
|
|
0
|
my $error_message = __readErrorMessageFromResponse($response); |
99
|
0
|
|
|
|
|
0
|
croak "Can't download file id: $file_id to destination file: $dest_file. Code: $response_code. Message: '$error_message'"; |
100
|
|
|
|
|
|
|
} |
101
|
1
|
|
|
|
|
23
|
return 1; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub deleteFile { |
105
|
1
|
|
|
1
|
1
|
944
|
my ($self, %opt) = @_; |
106
|
1
|
|
33
|
|
|
6
|
my $file_id = $opt{-file_id} || croak "You must specify '-file_id' param"; |
107
|
1
|
|
|
|
|
5
|
my $access_token = $self->__getAccessToken(); |
108
|
|
|
|
|
|
|
|
109
|
1
|
|
|
|
|
14
|
my $uri = URI->new(join('/', $FILE_API_URL, $file_id)); |
110
|
|
|
|
|
|
|
|
111
|
1
|
|
|
|
|
405
|
my $headers = [ |
112
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
113
|
|
|
|
|
|
|
]; |
114
|
|
|
|
|
|
|
|
115
|
1
|
|
|
|
|
10
|
my $request = HTTP::Request->new( 'DELETE', |
116
|
|
|
|
|
|
|
$uri, |
117
|
|
|
|
|
|
|
$headers, |
118
|
|
|
|
|
|
|
); |
119
|
1
|
|
|
|
|
136
|
my $response = $self->{ua}->request($request); |
120
|
1
|
|
|
|
|
639894
|
my $response_code = $response->code(); |
121
|
1
|
50
|
|
|
|
16
|
if ($response_code =~ /^[^2]/) { |
122
|
0
|
|
|
|
|
0
|
my $error_message = __readErrorMessageFromResponse($response); |
123
|
0
|
|
|
|
|
0
|
croak "Can't delete file id: $file_id. Code: $response_code. Message: $error_message"; |
124
|
|
|
|
|
|
|
} |
125
|
1
|
|
|
|
|
22
|
return 1; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub uploadFile { |
129
|
1
|
|
|
1
|
1
|
1194
|
my ($self, %opt) = @_; |
130
|
1
|
|
33
|
|
|
21
|
my $source_file = $opt{-source_file} || croak "You must specify '-source_file' param"; |
131
|
|
|
|
|
|
|
|
132
|
1
|
50
|
|
|
|
27
|
if (not -f $source_file) { |
133
|
0
|
|
|
|
|
0
|
croak "File: $source_file not exists"; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
1
|
|
|
|
|
14
|
my $file_size = (stat $source_file)[7]; |
137
|
1
|
|
|
|
|
9
|
my $part_upload_uri = $self->__createEmptyFile($source_file, $file_size, $opt{-parents}); |
138
|
1
|
50
|
|
|
|
91
|
open my $FH, "<$source_file" or croak "Can't open file: $source_file $!"; |
139
|
1
|
|
|
|
|
6
|
binmode $FH; |
140
|
|
|
|
|
|
|
|
141
|
1
|
|
|
|
|
2
|
my $filebuf; |
142
|
1
|
|
|
|
|
502
|
my $uri = URI->new($part_upload_uri); |
143
|
1
|
|
|
|
|
247
|
my $start_byte = 0; |
144
|
1
|
|
|
|
|
522
|
while (my $bytes = read($FH, $filebuf, $UPLOAD_BUFF_SIZE)) { |
145
|
4
|
|
|
|
|
21
|
my $end_byte = $start_byte + $bytes - 1; |
146
|
4
|
|
|
|
|
33
|
my $headers = [ |
147
|
|
|
|
|
|
|
'Content-Length' => $bytes, |
148
|
|
|
|
|
|
|
'Content-Range' => sprintf("bytes %d-%d/%d", $start_byte, $end_byte, $file_size), |
149
|
|
|
|
|
|
|
]; |
150
|
4
|
|
|
|
|
42
|
my $request = HTTP::Request->new('PUT', $uri, $headers, $filebuf); |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# Send request to upload part of file |
153
|
4
|
|
|
|
|
1430
|
my $response = $self->{ua}->request($request); |
154
|
4
|
|
|
|
|
1478671
|
my $response_code = $response->code(); |
155
|
|
|
|
|
|
|
# On end part, response code is 200, on middle part is 308 |
156
|
4
|
100
|
66
|
|
|
99
|
if ($response_code == 200 || $response_code == 201) { |
|
|
50
|
|
|
|
|
|
157
|
1
|
50
|
|
|
|
5
|
if ($end_byte + 1 != $file_size) { |
158
|
0
|
|
|
|
|
0
|
croak "Server return code: $response_code on upload file, but file is not fully uploaded. End byte: $end_byte. File size: $file_size. File: $source_file"; |
159
|
|
|
|
|
|
|
} |
160
|
1
|
|
|
|
|
6
|
return decode_json($response->content()); |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
elsif ($response_code != 308) { |
163
|
0
|
|
|
|
|
0
|
croak "Wrong response code on upload part file. Code: $response_code. File: $source_file"; |
164
|
|
|
|
|
|
|
} |
165
|
3
|
|
|
|
|
681
|
$start_byte += $bytes; |
166
|
|
|
|
|
|
|
} |
167
|
0
|
|
|
|
|
0
|
close $FH; |
168
|
|
|
|
|
|
|
|
169
|
0
|
|
|
|
|
0
|
return; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub setFilePermission { |
173
|
3
|
|
|
3
|
1
|
1710
|
my ($self, %opt) = @_; |
174
|
3
|
|
33
|
|
|
13
|
my $file_id = $opt{-file_id} || croak "You must specify '-file_id' param"; |
175
|
3
|
|
33
|
|
|
11
|
my $permission_type = $opt{-type} || croak "You must specify '-type' param"; |
176
|
3
|
|
33
|
|
|
8
|
my $role = $opt{-role} || croak "You must specify '-role' param"; |
177
|
3
|
|
|
|
|
16
|
my %valid_permissions = ( |
178
|
|
|
|
|
|
|
'user' => 1, |
179
|
|
|
|
|
|
|
'group' => 1, |
180
|
|
|
|
|
|
|
'domain' => 1, |
181
|
|
|
|
|
|
|
'anyone' => 1, |
182
|
|
|
|
|
|
|
); |
183
|
|
|
|
|
|
|
|
184
|
3
|
|
|
|
|
15
|
my %valid_roles = ( |
185
|
|
|
|
|
|
|
'owner' => 1, |
186
|
|
|
|
|
|
|
'organizer' => 1, |
187
|
|
|
|
|
|
|
'fileOrganizer' => 1, |
188
|
|
|
|
|
|
|
'writer' => 1, |
189
|
|
|
|
|
|
|
'commenter' => 1, |
190
|
|
|
|
|
|
|
'reader' => 1, |
191
|
|
|
|
|
|
|
); |
192
|
|
|
|
|
|
|
#Check permission in param |
193
|
3
|
100
|
|
|
|
9
|
if (not $valid_permissions{$permission_type}) { |
194
|
1
|
|
|
|
|
167
|
croak "Wrong permission type: '$permission_type'. Valid permissions types: " . join(' ', keys %valid_permissions); |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
#Check role in param |
198
|
2
|
50
|
|
|
|
32
|
if (not $valid_roles{$role}) { |
199
|
0
|
|
|
|
|
0
|
croak "Wrong role: '$role'. Valid roles: " . join(' ', keys %valid_roles); |
200
|
|
|
|
|
|
|
} |
201
|
2
|
|
|
|
|
11
|
my $access_token = $self->__getAccessToken(); |
202
|
|
|
|
|
|
|
|
203
|
2
|
|
|
|
|
27
|
my $uri = URI->new(join('/', $FILE_API_URL, $file_id, 'permissions')); |
204
|
2
|
|
|
|
|
274
|
my $headers = [ |
205
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
206
|
|
|
|
|
|
|
'Content-Type' => 'application/json', |
207
|
|
|
|
|
|
|
]; |
208
|
2
|
|
|
|
|
11
|
my $request_content = { |
209
|
|
|
|
|
|
|
'type' => $permission_type, |
210
|
|
|
|
|
|
|
'role' => $role, |
211
|
|
|
|
|
|
|
}; |
212
|
|
|
|
|
|
|
|
213
|
2
|
|
|
|
|
30
|
my $request = HTTP::Request->new('POST', $uri, $headers, encode_json($request_content)); |
214
|
|
|
|
|
|
|
|
215
|
2
|
|
|
|
|
372
|
my $response = $self->{ua}->request($request); |
216
|
2
|
|
|
|
|
1733043
|
my $response_code = $response->code(); |
217
|
2
|
50
|
|
|
|
27
|
if ($response_code != 200) { |
218
|
0
|
|
|
|
|
0
|
my $error_message = __readErrorMessageFromResponse($response); |
219
|
0
|
|
|
|
|
0
|
croak "Can't share file id: $file_id. Code: $response_code. Error message: $error_message"; |
220
|
|
|
|
|
|
|
} |
221
|
2
|
|
|
|
|
10
|
return decode_json($response->content()); |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub getFileMetadata { |
225
|
2
|
|
|
2
|
1
|
548
|
my ($self, %opt) = @_; |
226
|
2
|
|
33
|
|
|
11
|
my $file_id = $opt{-file_id} || croak "You must specify '-file_id' param"; |
227
|
2
|
|
|
|
|
10
|
my $access_token = $self->__getAccessToken(); |
228
|
|
|
|
|
|
|
|
229
|
2
|
|
|
|
|
28
|
my $uri = URI->new(join('/', $FILE_API2_URL, $file_id)); |
230
|
2
|
|
|
|
|
239
|
$uri->query_form('supportsTeamDrives' => 'true'); |
231
|
|
|
|
|
|
|
|
232
|
2
|
|
|
|
|
180
|
my $headers = [ |
233
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
234
|
|
|
|
|
|
|
]; |
235
|
2
|
|
|
|
|
16
|
my $request = HTTP::Request->new("GET", $uri, $headers); |
236
|
2
|
|
|
|
|
272
|
my $response = $self->{ua}->request($request); |
237
|
2
|
|
|
|
|
519518
|
my $response_code = $response->code(); |
238
|
2
|
50
|
|
|
|
27
|
if ($response_code != 200) { |
239
|
0
|
|
|
|
|
0
|
my $error_message = __readErrorMessageFromResponse($response); |
240
|
0
|
|
|
|
|
0
|
croak "Can't get metadata from file id: $file_id. Code: $response_code. Error message: $error_message"; |
241
|
|
|
|
|
|
|
} |
242
|
2
|
|
|
|
|
8
|
return decode_json($response->content()); |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub shareFile { |
246
|
1
|
|
|
1
|
1
|
1283
|
my ($self, %opt) = @_; |
247
|
1
|
|
33
|
|
|
5
|
my $file_id = $opt{-file_id} || croak "You must specify '-file_id' param"; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
## Adding permissions to file |
250
|
1
|
|
|
|
|
7
|
my $permission = $self->setFilePermission( |
251
|
|
|
|
|
|
|
-file_id => $file_id, |
252
|
|
|
|
|
|
|
-type => 'anyone', |
253
|
|
|
|
|
|
|
-role => 'reader', |
254
|
|
|
|
|
|
|
); |
255
|
1
|
50
|
33
|
|
|
65
|
if ((not exists $permission->{type}) || ($permission->{type} ne 'anyone')) { |
256
|
0
|
|
|
|
|
0
|
croak "Can't set permission to anyone for file id: $file_id"; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
1
|
|
|
|
|
378
|
my $metadata = $self->getFileMetadata( -file_id => $file_id ); |
260
|
1
|
|
|
|
|
147
|
return $metadata->{webContentLink}; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub __createEmptyFile { |
264
|
1
|
|
|
1
|
|
5
|
my ($self, $source_file, $file_size, $parents) = @_; |
265
|
1
|
|
|
|
|
5
|
my $access_token = $self->__getAccessToken(); |
266
|
|
|
|
|
|
|
|
267
|
1
|
|
|
|
|
58
|
my $body = { |
268
|
|
|
|
|
|
|
'name' => basename($source_file), |
269
|
|
|
|
|
|
|
}; |
270
|
1
|
50
|
|
|
|
5
|
$body->{parents} = $parents if $parents; |
271
|
1
|
|
|
|
|
9
|
my $body_json = encode_json($body); |
272
|
|
|
|
|
|
|
|
273
|
1
|
|
|
|
|
12
|
my $uri = URI->new($UPLOAD_FILE_API_URL); |
274
|
1
|
|
|
|
|
114
|
$uri->query_form('upload_type' => 'resumable'); |
275
|
1
|
|
|
|
|
93
|
my $headers = [ |
276
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
277
|
|
|
|
|
|
|
'Content-Length' => length($body_json), |
278
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
279
|
|
|
|
|
|
|
'X-Upload-Content-Length' => $file_size, |
280
|
|
|
|
|
|
|
]; |
281
|
|
|
|
|
|
|
|
282
|
1
|
|
|
|
|
8
|
my $request = HTTP::Request->new('POST', $uri, $headers, $body_json); |
283
|
1
|
|
|
|
|
236
|
my $response = $self->{ua}->request($request); |
284
|
|
|
|
|
|
|
|
285
|
1
|
|
|
|
|
240571
|
my $response_code = $response->code(); |
286
|
1
|
50
|
|
|
|
13
|
if ($response_code != 200) { |
287
|
0
|
|
|
|
|
0
|
my $error_message = __readErrorMessageFromResponse($response); |
288
|
0
|
|
|
|
|
0
|
croak "Can't upload part of file. Code: $response_code. Error message: $error_message"; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
1
|
50
|
|
|
|
5
|
my $location = $response->header('Location') or croak "Location header not defined"; |
292
|
|
|
|
|
|
|
|
293
|
1
|
|
|
|
|
64
|
return $location; |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub __readErrorMessageFromResponse { |
297
|
0
|
|
|
0
|
|
0
|
my ($response) = @_; |
298
|
0
|
|
|
|
|
0
|
my $error_message = eval {decode_json($response->content)}; |
|
0
|
|
|
|
|
0
|
|
299
|
0
|
0
|
|
|
|
0
|
if ($error_message) { |
300
|
0
|
|
|
|
|
0
|
return $error_message->{error}->{message}; |
301
|
|
|
|
|
|
|
} |
302
|
0
|
|
|
|
|
0
|
return ''; |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
sub __searchFile { |
308
|
3
|
|
|
3
|
|
9
|
my ($self, $q) = @_; |
309
|
|
|
|
|
|
|
|
310
|
3
|
|
|
|
|
12
|
my $access_token = $self->__getAccessToken(); |
311
|
|
|
|
|
|
|
|
312
|
3
|
|
|
|
|
16
|
my $headers = [ |
313
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
314
|
|
|
|
|
|
|
]; |
315
|
|
|
|
|
|
|
|
316
|
3
|
|
|
|
|
27
|
my $uri = URI->new($FILE_API_URL); |
317
|
3
|
|
|
|
|
345
|
$uri->query_form('q' => $q); |
318
|
3
|
|
|
|
|
459
|
my $request = HTTP::Request->new('GET', |
319
|
|
|
|
|
|
|
$uri, |
320
|
|
|
|
|
|
|
$headers, |
321
|
|
|
|
|
|
|
); |
322
|
3
|
|
|
|
|
391
|
my $files = []; |
323
|
3
|
|
|
|
|
15
|
$self->__apiRequest($request, $files); |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
|
326
|
3
|
|
|
|
|
28
|
return $files; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
sub __apiRequest { |
330
|
3
|
|
|
3
|
|
11
|
my ($self, $request, $files) = @_; |
331
|
|
|
|
|
|
|
|
332
|
3
|
|
|
|
|
15
|
my $response = $self->{ua}->request($request); |
333
|
3
|
|
|
|
|
1075245
|
my $response_code = $response->code; |
334
|
3
|
50
|
|
|
|
42
|
if ($response_code != 200) { |
335
|
0
|
|
|
|
|
0
|
croak "Wrong response code on search_file. Code: $response_code"; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
3
|
|
|
|
|
15
|
my $json_res = decode_json($response->content); |
339
|
|
|
|
|
|
|
|
340
|
3
|
50
|
|
|
|
536
|
if (my $next_token = $json_res->{next_token}) { |
341
|
0
|
|
|
|
|
0
|
my $uri = $request->uri; |
342
|
0
|
|
|
|
|
0
|
$uri->query_form('next_token' => $next_token); |
343
|
0
|
|
|
|
|
0
|
$self->__apiRequest($request, $files); |
344
|
|
|
|
|
|
|
} |
345
|
3
|
|
|
|
|
6
|
push @$files, @{$json_res->{files}}; |
|
3
|
|
|
|
|
12
|
|
346
|
|
|
|
|
|
|
|
347
|
3
|
|
|
|
|
42
|
return 1; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub __getAccessToken { |
351
|
10
|
|
|
10
|
|
27
|
my ($self) = @_; |
352
|
|
|
|
|
|
|
|
353
|
10
|
|
|
|
|
33
|
my $oauth = $self->{oauth}; |
354
|
|
|
|
|
|
|
my $token_info = |
355
|
10
|
|
|
|
|
19
|
eval { |
356
|
10
|
|
|
|
|
72
|
$oauth->getTokenInfo( -access_token => $self->{access_token} ); |
357
|
|
|
|
|
|
|
}; |
358
|
|
|
|
|
|
|
# If error on get token info or token is expired |
359
|
10
|
100
|
|
|
|
1181493
|
if (not $@) { |
360
|
9
|
50
|
33
|
|
|
102
|
if ((exists $token_info->{expires_in}) && ($token_info->{expires_in} > 5)) { |
361
|
9
|
|
|
|
|
58
|
return $self->{access_token}; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
#Refresh token |
366
|
1
|
|
|
|
|
9
|
$oauth->refreshToken( -refresh_token => $self->{refresh_token} ); |
367
|
1
|
|
|
|
|
122191
|
$self->{refresh_token} = $oauth->getRefreshToken(); |
368
|
1
|
|
|
|
|
8
|
$self->{access_token} = $oauth->getAccessToken(); |
369
|
|
|
|
|
|
|
|
370
|
1
|
|
|
|
|
7
|
return $self->{access_token}; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
1; |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=encoding utf8 |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
=head1 NAME |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
B - simple Google drive API module |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=head1 SYNOPSIS |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
This module use to upload, download, share file on Google drive |
384
|
|
|
|
|
|
|
use Net::Google::Drive; |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
#Create disk object. You need send in param 'access_token', 'refresh_token', 'client_id' and 'client_secret'. |
387
|
|
|
|
|
|
|
#Values of 'client_id' and 'client_secret' uses to create Net::Google::OAuth object so that update value of 'access_token'. |
388
|
|
|
|
|
|
|
my $disk = Net::Google::Drive->new( |
389
|
|
|
|
|
|
|
-client_id => $client_id, |
390
|
|
|
|
|
|
|
-client_secret => $client_secret, |
391
|
|
|
|
|
|
|
-access_token => $access_token, |
392
|
|
|
|
|
|
|
-refresh_token => $refresh_token, |
393
|
|
|
|
|
|
|
); |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
# Search file by name |
396
|
|
|
|
|
|
|
my $file_name = 'upload.doc'; |
397
|
|
|
|
|
|
|
my $files = $disk->searchFileByName( -filename => $file_name ) or croak "File '$file_name' not found"; |
398
|
|
|
|
|
|
|
my $file_id = $files->[0]->{id}; |
399
|
|
|
|
|
|
|
print "File id: $file_id\n"; |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
#Download file |
402
|
|
|
|
|
|
|
my $dest_file = '/home/upload.doc'; |
403
|
|
|
|
|
|
|
$disk->downloadFile( |
404
|
|
|
|
|
|
|
-file_id => $file_id, |
405
|
|
|
|
|
|
|
-dest_file => $dest_file, |
406
|
|
|
|
|
|
|
); |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
#Upload file |
409
|
|
|
|
|
|
|
my $source_file = '/home/upload.doc'; |
410
|
|
|
|
|
|
|
my $res = $disk->uploadFile( -source_file => $source_file ); |
411
|
|
|
|
|
|
|
print "File: $source_file uploaded. File id: $res->{id}\n"; |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=head1 METHODS |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
=head2 new(%opt) |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
Create L object |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
%opt: |
420
|
|
|
|
|
|
|
-client_id => Your app client id (Get from google when register your app) |
421
|
|
|
|
|
|
|
-client_secret => Your app client secret (Get from google when register your app) |
422
|
|
|
|
|
|
|
-access_token => Access token value (Get from L) |
423
|
|
|
|
|
|
|
-refresh_token => Refresh token value (Get from L) |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=head2 searchFileByName(%opt) |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
Search file on google disk by name. Return arrayref to info with found files. If files not found - return empty arrayref |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
%opt: |
430
|
|
|
|
|
|
|
-filename => Name of file to find |
431
|
|
|
|
|
|
|
Return: |
432
|
|
|
|
|
|
|
[ |
433
|
|
|
|
|
|
|
[0] { |
434
|
|
|
|
|
|
|
id "1f13sLfo6UEyUuFpn-NWPnY", |
435
|
|
|
|
|
|
|
kind "drive#file", |
436
|
|
|
|
|
|
|
mimeType "application/x-perl", |
437
|
|
|
|
|
|
|
name "drive.t" |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
] |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=head2 searchFileByNameContains(%opt) |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
Search files on google drive by name contains value in param '-filename' |
444
|
|
|
|
|
|
|
Param and return value same as in method L |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head2 downloadFile(%opt) |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
Download file from google dist to -dest_file on local system. Return 1 if success, die in otherwise |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
%opt: |
451
|
|
|
|
|
|
|
-dest_file => Name of file on disk in which you will download file from google disk |
452
|
|
|
|
|
|
|
-file_id => Id of file on google disk |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head2 deleteFile(%opt) |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
Delete file from google disk. Return 1 if success, die in otherwise |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
%opt: |
459
|
|
|
|
|
|
|
-file_id => Id of file on google disk |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head2 uploadFile(%opt) |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
Upload file from local system to google drive. Return file_info hashref if success, die in otherwise |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
%opt: |
466
|
|
|
|
|
|
|
-source_file => File on local system |
467
|
|
|
|
|
|
|
-parents => Optional arrayref of parent ids |
468
|
|
|
|
|
|
|
Return: |
469
|
|
|
|
|
|
|
{ |
470
|
|
|
|
|
|
|
id "1LVAr2PpqX9m314JyZ6YJ4v_KIzG0Gey2", |
471
|
|
|
|
|
|
|
kind "drive#file", |
472
|
|
|
|
|
|
|
mimeType "application/octet-stream", |
473
|
|
|
|
|
|
|
name "gogle_upload_file" |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
=head2 setFilePermission(%opt) |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Set permissions for file on google drive. Return permission hashref, die in otherwise |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
%opt: |
481
|
|
|
|
|
|
|
-file_id => Id of file on google disk |
482
|
|
|
|
|
|
|
-type => The type of the grantee. Valid values are: (user, group, domain, anyone) |
483
|
|
|
|
|
|
|
-role => The role granted by this permission. Valid values are: (owner, organizer, fileOrganizer, writer, commenter, reader) |
484
|
|
|
|
|
|
|
Return: |
485
|
|
|
|
|
|
|
{ |
486
|
|
|
|
|
|
|
allowFileDiscovery JSON::PP::Boolean { |
487
|
|
|
|
|
|
|
Parents Types::Serialiser::BooleanBase |
488
|
|
|
|
|
|
|
public methods (0) |
489
|
|
|
|
|
|
|
private methods (0) |
490
|
|
|
|
|
|
|
internals: 0 |
491
|
|
|
|
|
|
|
}, |
492
|
|
|
|
|
|
|
id "anyoneWithLink", |
493
|
|
|
|
|
|
|
kind "drive#permission", |
494
|
|
|
|
|
|
|
role "reader", |
495
|
|
|
|
|
|
|
type "anyone" |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=head2 getFileMetadata(%opt) |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Get metadata of file. Return hashref with metadata if success, die in otherwise |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
%opt: |
503
|
|
|
|
|
|
|
-file_id => Id of file on google disk |
504
|
|
|
|
|
|
|
Return: |
505
|
|
|
|
|
|
|
{ alternateLink "https://drive.google.com/file/d/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut/view?usp=drivesdk", |
506
|
|
|
|
|
|
|
appDataContents JSON::PP::Boolean { |
507
|
|
|
|
|
|
|
Parents Types::Serialiser::BooleanBase |
508
|
|
|
|
|
|
|
public methods (0) |
509
|
|
|
|
|
|
|
private methods (0) |
510
|
|
|
|
|
|
|
internals: 0 |
511
|
|
|
|
|
|
|
}, |
512
|
|
|
|
|
|
|
capabilities { |
513
|
|
|
|
|
|
|
canCopy JSON::PP::Boolean { |
514
|
|
|
|
|
|
|
Parents Types::Serialiser::BooleanBase |
515
|
|
|
|
|
|
|
public methods (0) |
516
|
|
|
|
|
|
|
private methods (0) |
517
|
|
|
|
|
|
|
internals: 1 |
518
|
|
|
|
|
|
|
}, |
519
|
|
|
|
|
|
|
canEdit var{capabilities}{canCopy} |
520
|
|
|
|
|
|
|
}, |
521
|
|
|
|
|
|
|
copyable var{capabilities}{canCopy}, |
522
|
|
|
|
|
|
|
copyRequiresWriterPermission var{appDataContents}, |
523
|
|
|
|
|
|
|
createdDate "2018-10-04T12:05:15.896Z", |
524
|
|
|
|
|
|
|
downloadUrl "https://doc-0g-7o-docs.googleusercontent.com/docs/securesc/ck8i7vfbvef13kb30b8mkrcjv4ihp2uj/3mfn1kbr655euhlo7tctg5mmn8oirg |
525
|
|
|
|
|
|
|
gf/1538654400000/10526805100525201667/10526805100525201667/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut?e=download&gd=true", |
526
|
|
|
|
|
|
|
editable var{capabilities}{canCopy}, |
527
|
|
|
|
|
|
|
embedLink "https://drive.google.com/file/d/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut/preview?usp=drivesdk", |
528
|
|
|
|
|
|
|
etag ""omwGuTP8OdxhZkubyp-j43cFdJQ/MTUzODY1NDcxNTg5Ng"", |
529
|
|
|
|
|
|
|
explicitlyTrashed var{appDataContents}, |
530
|
|
|
|
|
|
|
fileExtension "", |
531
|
|
|
|
|
|
|
fileSize 1000000, |
532
|
|
|
|
|
|
|
headRevisionId "0B4HgPHxdPy22UmZXSFVRTkRLbXhFakdzZjFSUGkrNWZIVFN3PQ", |
533
|
|
|
|
|
|
|
iconLink "https://drive-thirdparty.googleusercontent.com/16/type/application/octet-stream", |
534
|
|
|
|
|
|
|
id "10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut", |
535
|
|
|
|
|
|
|
kind "drive#file", |
536
|
|
|
|
|
|
|
labels { |
537
|
|
|
|
|
|
|
hidden var{appDataContents}, |
538
|
|
|
|
|
|
|
restricted var{appDataContents}, |
539
|
|
|
|
|
|
|
starred var{appDataContents}, |
540
|
|
|
|
|
|
|
trashed var{appDataContents}, |
541
|
|
|
|
|
|
|
viewed var{appDataContents} |
542
|
|
|
|
|
|
|
}, |
543
|
|
|
|
|
|
|
lastModifyingUser { |
544
|
|
|
|
|
|
|
displayName "Ларри Уолл", |
545
|
|
|
|
|
|
|
emailAddress "perlgogledrivemodule@gmail.com", |
546
|
|
|
|
|
|
|
isAuthenticatedUser var{capabilities}{canCopy}, |
547
|
|
|
|
|
|
|
kind "drive#user", |
548
|
|
|
|
|
|
|
permissionId 10526805100525201667 |
549
|
|
|
|
|
|
|
}, |
550
|
|
|
|
|
|
|
lastModifyingUserName "Ларри Уолл", |
551
|
|
|
|
|
|
|
markedViewedByMeDate "1970-01-01T00:00:00.000Z", |
552
|
|
|
|
|
|
|
md5Checksum "ded2a2983b3e1743152d8224549510e1", |
553
|
|
|
|
|
|
|
mimeType "application/octet-stream", |
554
|
|
|
|
|
|
|
modifiedByMeDate "2018-10-04T12:05:15.896Z", |
555
|
|
|
|
|
|
|
modifiedDate "2018-10-04T12:05:15.896Z", |
556
|
|
|
|
|
|
|
originalFilename "gogle_upload_file", |
557
|
|
|
|
|
|
|
ownerNames [ |
558
|
|
|
|
|
|
|
[0] "Ларри Уолл" |
559
|
|
|
|
|
|
|
], |
560
|
|
|
|
|
|
|
owners [ |
561
|
|
|
|
|
|
|
[0] { |
562
|
|
|
|
|
|
|
displayName "Ларри Уолл", |
563
|
|
|
|
|
|
|
emailAddress "perlgogledrivemodule@gmail.com", |
564
|
|
|
|
|
|
|
isAuthenticatedUser var{capabilities}{canCopy}, |
565
|
|
|
|
|
|
|
kind "drive#user", |
566
|
|
|
|
|
|
|
permissionId 10526805100525201667 |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
], |
569
|
|
|
|
|
|
|
parents [ |
570
|
|
|
|
|
|
|
[0] { |
571
|
|
|
|
|
|
|
id "0AIHgPHxdPy22Uk9PVA", |
572
|
|
|
|
|
|
|
isRoot var{capabilities}{canCopy}, |
573
|
|
|
|
|
|
|
kind "drive#parentReference", |
574
|
|
|
|
|
|
|
parentLink "https://www.googleapis.com/drive/v2/files/0AIHgPHxdPy22Uk9PVA", |
575
|
|
|
|
|
|
|
selfLink "https://www.googleapis.com/drive/v2/files/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut/parents/0AIHgPHxdPy22Uk9PVA" |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
], |
578
|
|
|
|
|
|
|
quotaBytesUsed 1000000, |
579
|
|
|
|
|
|
|
selfLink "https://www.googleapis.com/drive/v2/files/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut", |
580
|
|
|
|
|
|
|
shared var{appDataContents}, |
581
|
|
|
|
|
|
|
spaces [ |
582
|
|
|
|
|
|
|
[0] "drive" |
583
|
|
|
|
|
|
|
], |
584
|
|
|
|
|
|
|
title "gogle_upload_file", |
585
|
|
|
|
|
|
|
userPermission { |
586
|
|
|
|
|
|
|
etag ""omwGuTP8OdxhZkubyp-j43cFdJQ/N52l-iUAo-dARaTch8nQXOzl348"", |
587
|
|
|
|
|
|
|
id "me", |
588
|
|
|
|
|
|
|
kind "drive#permission", |
589
|
|
|
|
|
|
|
role "owner", |
590
|
|
|
|
|
|
|
selfLink "https://www.googleapis.com/drive/v2/files/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut/permissions/me", |
591
|
|
|
|
|
|
|
type "user" |
592
|
|
|
|
|
|
|
}, |
593
|
|
|
|
|
|
|
version 2, |
594
|
|
|
|
|
|
|
webContentLink "https://drive.google.com/uc?id=10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut&export=download", |
595
|
|
|
|
|
|
|
writersCanShare var{capabilities}{canCopy} |
596
|
|
|
|
|
|
|
} |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=head2 shareFile(%opt) |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
Share file for download. Return download link if success, die in otherwise |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
%opt: |
603
|
|
|
|
|
|
|
-file_id => Id of file on google disk |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=head1 DEPENDENCE |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
L, L, L, L, L, L |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=head1 AUTHORS |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
=over 4 |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=item * |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
Pavel Andryushin |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
=back |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
This software is copyright (c) 2018 by Pavel Andryushin. |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
624
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=cut |