source: extensions/pLoader/trunk/src/Uploader/PWG/WebServices.pm @ 4610

Revision 4610, 16.8 KB checked in by ronosman, 10 years ago (diff)

Bug 1362 fixed : cannot connect to Piwigo when hosted by free.fr

  • Property svn:eol-style set to LF
Line 
1# +-----------------------------------------------------------------------+
2# | pLoader - a Perl photo uploader for Piwigo                            |
3# +-----------------------------------------------------------------------+
4# | Copyright(C) 2008      Piwigo Team                  http://piwigo.org |
5# +-----------------------------------------------------------------------+
6# | This program is free software; you can redistribute it and/or modify  |
7# | it under the terms of the GNU General Public License as published by  |
8# | the Free Software Foundation                                          |
9# |                                                                       |
10# | This program is distributed in the hope that it will be useful, but   |
11# | WITHOUT ANY WARRANTY; without even the implied warranty of            |
12# | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
13# | General Public License for more details.                              |
14# |                                                                       |
15# | You should have received a copy of the GNU General Public License     |
16# | along with this program; if not, write to the Free Software           |
17# | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
18# | USA.                                                                  |
19# +-----------------------------------------------------------------------+
20package Uploader::PWG::WebServices;
21 
22use strict;
23use warnings;
24use MIME::Base64 qw(encode_base64); 
25use JSON;
26use LWP::UserAgent;
27use Data::Dumper;
28use Digest::MD5::File qw/file_md5_hex md5_hex/;
29use File::Slurp;
30use File::Spec;
31use POSIX qw(ceil floor);
32use base qw/
33           Uploader::Object
34           Class::Accessor::Fast
35           /;
36
37__PACKAGE__->mk_accessors( 
38    qw/
39           uagent
40           urlbase
41           username
42           password
43           qry_list_categories
44           qry_add_categories
45           qry_list_tags
46           items
47           tags
48           categories
49           site_high_file
50           site_resized_file
51           site_thumb_file
52           site_image_name
53           site_tags
54           rank
55           site_author
56           site_comment
57           site_img_date_creation
58           uagent_response
59           login_result
60           action_result
61           upload_high
62           chunk_size
63           sum
64           image_id
65           typecode
66                   reupload_action_files
67                   reupload_action_properties
68                   reupload_action_properties_m
69                   single_value_mode
70                   multiple_value_mode
71                   privacy_level
72      / 
73);
74
75$|=1;
76
77sub Init {
78    my ( $self, $version ) = @_ ;
79
80    # to transform a type_code into a typename
81    $self->typecode(
82        {
83             file => 'file',
84             thumbnail => 'thumb',
85             high => 'high',   
86        }
87    );
88       
89        $self->single_value_mode(
90            {
91                    1 => 'fill_if_empty',
92                        2 => 'replace',
93                }
94        );
95
96        $self->multiple_value_mode(
97            {
98                    1 => 'append',
99                        2 => 'replace',
100                }
101        );
102       
103    $self->uagent(
104        LWP::UserAgent->new(
105            agent => sprintf("Mozilla/pLoader %s", $version)       
106        )
107    );
108   
109    $self->uagent->cookie_jar({});     
110
111    $self->urlbase(
112        $self->{site_url}
113    );
114   
115    $self->username(
116        $self->{site_username}
117    );
118   
119    $self->password(
120        $self->{site_password}
121    );
122   
123    $self->chunk_size(
124        $self->{chunk_size}
125    );
126
127   
128    $self->uagent->default_headers->authorization_basic(
129        $self->{http_username}||$self->username, 
130        $self->{http_password}||$self->password
131    );
132   
133   
134    $self->qry_list_categories( sprintf
135        "%s/ws.php?format=json&method=%s&recursive=%s",
136        $self->urlbase,
137#        'pwg.categories.getAdminList',
138        'pwg.categories.getList',
139        'true',
140    );
141
142    $self->qry_list_tags( sprintf
143        "%s/ws.php?format=json&method=%s",
144        $self->urlbase,
145        'pwg.tags.getAdminList',
146    );
147
148
149    my $form = {
150        method => 'pwg.session.login',
151        username => $self->username,
152        password => $self->password,
153    };
154 
155    $self->_execute_post(
156        $form
157    );   
158
159    $self->login_result(
160        $self->_json_response_content
161    );
162
163}
164
165sub _json_response_content {
166    my ( $self ) = @_;
167
168    my $hresult = {} ;
169
170    if($self->uagent_response->is_success){
171        eval {
172            $hresult = from_json(
173                $self->uagent_response->content
174            );
175        };
176                if($@){
177            # when server response has warnings, the content response is not a valid json string
178                    # find the json response
179            $self->uagent_response->content =~ /(\{.+\})/;
180                    #printf("json response %s\n", $1);
181            eval {
182                $hresult = from_json(
183                    $1
184                );
185            };
186        }               
187    }
188    else{
189        $hresult = {
190            'message' => $self->uagent_response->message,
191            'stat'    => 'fail',
192        };
193    }
194       
195        $hresult;
196}
197
198sub _execute_get {
199    my ( $self, $query ) = @_;
200
201    eval {
202        $self->uagent_response(
203            $self->uagent->get(
204                $query
205                    )
206        );
207    };
208
209    if($@){
210        printf("An error occured in query execution %s\n%s",
211            $query,
212            $@,
213        );     
214    }
215}
216
217sub _execute_post {
218    my ( $self, $form ) = @_;
219
220    my $result;
221    eval {
222        $result = $self->uagent_response(
223            $self->uagent->post(
224                $self->urlbase.'/ws.php?format=json',
225                $form
226            )
227        );
228        };
229
230    if($@){
231        printf("An error occured in post execution %s\n%s",
232            $form->{method},
233            $@,
234        );     
235    }
236       
237        return ( $result->is_success, $result->status_line );
238}
239sub GetCategories {
240    my ( $self ) = @_;
241
242 
243    $self->_execute_get(
244            $self->qry_list_categories
245        );
246    $self->_json_response_content->{result}{categories};
247}
248
249sub GetTags {
250    my ( $self ) = @_;
251
252    $self->_execute_get(
253        $self->qry_list_tags   
254        );
255    $self->_json_response_content->{result}{tags};
256}
257
258sub AddTags {
259    my ( $self, $name ) = @_;
260
261    my $form = {
262        method            => 'pwg.tags.add',
263        name              => $name,
264       
265    };
266
267    return ( $self->_execute_post($form), $self->_json_response_content );
268} 
269
270
271sub UploadImage {
272    my ( $self, $progress ) = @_;
273
274    $self->image_id(
275        $self->_exists($progress->{original_sum})
276    );
277
278    my $status = 1;
279    my $status_line ="OK";
280    my $content = {};
281    my $doubleCheck;
282    my $form;
283    UPLOAD: while(1){
284        # first upload
285        if(!defined($self->image_id)){ 
286            $doubleCheck = 1;
287            $self->_checksum_files($progress);
288            my @types = ('file', 'thumb');
289            #printf("WS upload_high %s\n", $self->upload_high);
290            push @types, 'high' if $self->upload_high;
291            map{
292                my $rval = $self->_send_chunks($_, $progress);
293                $status_line = $rval->{message};
294                last UPLOAD if !$rval->{ok};
295            } @types;
296         
297       
298            $form = {
299                method            => 'pwg.images.add',
300                original_sum      => $self->sum->{original},
301                file_sum          => $self->sum->{file},
302                thumbnail_sum     => $self->sum->{thumb},
303                categories        => $self->categories,
304                name              => $self->site_image_name,
305                author            => $self->site_author,
306                comment           => $self->site_comment,
307                date_creation     => $self->site_img_date_creation,
308                tag_ids           => $self->site_tags,
309               
310            };
311
312           $form->{high_sum} = $self->sum->{high} if $self->upload_high;
313                   
314           $progress->{yield}->();
315           ( $status, $status_line ) = $self->_execute_post($form);
316                }
317        # re-upload
318        else{
319            # need to check if files have changed
320            # and update image info
321            if($self->reupload_action_files){
322                $self->_checksum_files($progress);
323                my $files = $self->_check_files();
324                if(defined($files)){
325                    $self->_add_files($files, $progress);       
326                }
327            }
328
329            $form = {
330                    method        => 'pwg.images.setInfo',
331                    image_id      => $self->image_id,
332                    };
333            # update metadata info
334            if($self->reupload_action_properties){     
335                        #printf("reupload_action_properties %s\n", $self->reupload_action_properties);
336                $form->{name}          = $self->site_image_name;
337                $form->{author}        = $self->site_author;
338                $form->{comment}       = $self->site_comment;
339                $form->{date_creation} = $self->site_img_date_creation;
340                                $form->{single_value_mode}  = $self->single_value_mode->{$self->reupload_action_properties};
341                $form->{level} = $self->privacy_level ? 2**($self->privacy_level - 1) : 0;
342                    }
343            if($self->reupload_action_properties_m){   
344                            #printf("reupload_action_properties_m %s\n", $self->reupload_action_properties_m);
345                $form->{tag_ids} = $self->site_tags if $self->site_tags;
346                $form->{categories} = $self->categories;
347                                $form->{multiple_value_mode} = $self->multiple_value_mode->{$self->reupload_action_properties_m};
348            };
349            $progress->{yield}->();
350            ( $status, $status_line ) = $self->_execute_post($form); 
351
352                }
353
354        delete $form->{tag_ids} unless defined $self->site_tags;
355        delete $form->{tag_ids} if '' eq $self->site_tags;
356
357        $progress->{yield}->();
358        # for first upload
359        if($doubleCheck){
360                    $self->image_id(
361                            $self->_exists($progress->{original_sum})
362                        );
363            $content->{stat} = !defined $self->image_id  ? 'fail' : 'ok'; 
364            if(defined $self->image_id and defined $self->privacy_level){
365                    ( $status, $status_line, $content ) = $self->_set_privacy_level;
366                }
367        }
368
369                last UPLOAD;
370    }# UPLOAD
371   
372    return ( $status,  $status_line, $content);
373}
374
375sub _set_privacy_level {
376    my ( $self ) = @_;
377               
378    my $form = {
379        method        => 'pwg.images.setPrivacyLevel',
380        image_id      => $self->image_id,
381        level         => $self->privacy_level ? 2**($self->privacy_level - 1) : 0,
382    };
383        print Dumper $form;
384        my ( $status, $status_line ) = $self->_execute_post($form); 
385    my $hresult = $self->_json_response_content;
386
387    ($status, $status_line, $hresult );
388}
389
390sub _checksum_files {
391    my ( $self, $progress ) = @_;
392
393    $self->sum(
394        {
395            file => $self->_checksum(
396                        $self->site_resized_file,
397                        $progress
398                    ),
399            thumb => $self->_checksum(
400                             $self->site_thumb_file,
401                             $progress
402                         ),
403            original => $progress->{original_sum}
404        }
405    ); 
406
407            high => $self->_checksum(
408                        $self->site_high_file,
409                        $progress
410                    ),
411
412    $self->sum->{high} = $self->_checksum(
413                        $self->site_high_file,
414                        $progress
415    ) if $self->upload_high ;
416}
417
418sub _check_files {
419    my ( $self ) = @_;
420
421    my $form = {
422        method   => 'pwg.images.checkFiles',
423        image_id => $self->image_id,
424    };
425   
426    @$form{'thumbnail_sum', 'file_sum' } = (
427        $self->sum->{thumb},
428        $self->sum->{file},
429    );
430
431    if($self->upload_high){
432        $form->{high_sum} = $self->sum->{high};
433    }
434
435    $self->_execute_post($form);   
436    my $hresult = $self->_json_response_content;
437
438    my $rval = 'ok' eq $hresult->{stat} ? $hresult->{result} : undef;
439   
440    $rval;
441}
442
443# $files is returned by _check_files
444# {
445#     thumbnail => 'equals', 'differs', 'missing'
446#     file => 'equals', 'differs', 'missing'
447#     high => 'equals', 'differs', 'missing'
448#}
449
450sub _add_files {
451    my ( $self, $files, $progress ) = @_;
452
453    map{
454        $self->_add_file($_, $progress);
455    }
456    map{
457        $self->typecode->{$_};
458    } grep { 'equals' ne $files->{$_} } keys %$files ;
459       
460}
461
462sub _add_file {
463    my ( $self, $type_code, $progress ) = @_;
464
465    $self->_send_chunks(
466        $type_code,
467        $progress,
468    );
469
470    my $form = {
471        method => 'pwg.images.addFile',
472        image_id => $self->image_id,
473        type     => $type_code,
474        sum      => $self->sum->{$type_code},           
475    }; 
476
477    $self->_execute_post($form);   
478    my $hresult = $self->_json_response_content;
479
480    my $rval = 'ok' eq $hresult->{stat} ? $hresult->{result} : undef;
481   
482    $rval;
483}
484
485sub IsAlreadyUploaded {
486    my ( $self, $md5_sums ) = @_;
487       
488        # md5_sums is an array ref
489        $self->_execute_post({
490        method      => 'pwg.images.exist',
491        md5sum_list => join(',', @$md5_sums)
492            }
493        );
494
495        my $sums = $self->_json_response_content->{result};
496
497    $sums;
498}
499
500sub _exists {
501    my ( $self, $md5_sum ) = @_;
502
503    my $form = {
504        method            => 'pwg.images.exist',
505        md5sum_list       => $md5_sum,
506    };
507
508    $self->_execute_post($form);   
509    my $hresult = $self->_json_response_content;
510
511        $hresult->{result} = {} if 'HASH' ne ref $hresult->{result};
512    my $id = 'ok' eq $hresult->{stat} ? $hresult->{result}{$md5_sum} : undef ;
513
514    $id;
515} 
516
517sub _checksum {
518    my ( $self, $file, $progress ) = @_;
519    my $file_sum;
520
521    my $yield = $progress->{yield};
522
523    $yield->();
524    $progress->{msg_details}->(
525        sprintf(
526            "%s : %s", 
527            $progress->{checksum_msg}, 
528            $file
529        )
530    );
531    eval {
532        $file_sum = file_md5_hex(
533            $file
534        );
535    };
536    $yield->();
537
538    $file_sum;
539}
540
541sub _send_chunks {
542    my ( $self, $type_code, $progress ) = @_;
543
544    my $msg = {
545        thumb=>'thumbnail_msg',
546        file=>'resized_msg',
547        high=>'highdef_msg',
548    };
549
550    my $filepath = {
551        thumb=>$self->site_thumb_file,
552        file=>$self->site_resized_file,
553        high=>$self->site_high_file,
554    };
555
556    $progress->{current_msg} = $progress->{$msg->{$type_code}};
557    $progress->{yield}->();
558
559    my $params = {
560         filepath      => $filepath->{$type_code},
561         type          => $type_code,
562         original_sum  => $self->sum->{original},
563    };
564    #print Dumper $params;
565    $self->send_chunks(
566       $params,
567       $progress,
568    );
569    $progress->{yield}->();
570
571    $params;
572}
573
574sub AddCategories{
575    my ( $self, $name, $parentid ) = @_;
576
577    my $form = {
578        method            => 'pwg.categories.add',
579        name              => $name,
580        parent            => $parentid,
581       
582    };
583       
584    return ( $self->_execute_post($form), $self->_json_response_content );
585}
586
587sub SetInfoCategories{
588    my ( $self, $name, $comment, $parentid ) = @_;
589
590    my $form = {
591        method            => 'pwg.categories.setInfo',
592        name              => $name,
593        comment           => $comment,
594        category_id       => $parentid,
595       
596    };
597
598    return ( $self->_execute_post($form), $self->_json_response_content );
599}
600
601
602sub send_chunks {
603    my ( $self, $params, $progress ) = @_;
604
605    my $yield = $progress->{yield};
606    my ( $vol, $dir, $filename ) = File::Spec->splitpath($params->{filepath});
607
608    $yield->();
609    $progress->{bar}->(0);
610    $yield->();
611    $progress->{msg_details}->(
612        sprintf(
613            "%s : %s", 
614            $progress->{current_msg}, 
615            $filename
616        )
617    );
618
619
620    $yield->();
621    my $content = read_file(
622        $params->{filepath},
623        binmode => ':raw',
624    );
625    $yield->();
626
627    my $content_length = length($content);
628    my $nb_chunks = ceil($content_length / $self->chunk_size);
629
630    my $chunk_pos = 0;
631    my $chunk_id = 1;
632
633
634    while ($chunk_pos < $content_length) {
635
636        my $chunk = substr(
637            $content,
638            $chunk_pos,
639            $self->chunk_size
640        );
641        $chunk_pos += $self->chunk_size;
642        my $response = $self->uagent->post(
643            $self->urlbase.'/ws.php?format=json',
644            {
645                method       => 'pwg.images.addChunk',
646                data         => encode_base64($chunk),
647                original_sum => $params->{original_sum},
648                position     => $chunk_id,
649                type         => $params->{type},
650            }
651        );
652        $yield->();
653        $progress->{bar}->(100*($chunk_pos/$content_length));
654        $progress->{msg_details}->(
655            sprintf(
656                "%s : %s", 
657                $progress->{current_msg}, 
658                $filename
659            )
660        );
661        $params->{ok} = 1;
662        if ($response->code != 200) {
663            printf("response code    : %u\n", $response->code);
664            printf("response message : %s\n", $response->message);
665            $params->{ok} = 0;
666            $params->{message} = $response->message;
667            $params->{code} = $response->code;
668            last;
669        }
670
671        $chunk_id++;
672    }
673}
674
675 
6761;
677   
Note: See TracBrowser for help on using the repository browser.