File Coverage

blib/lib/jQuery/File/Upload/Imager.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package jQuery::File::Upload::Imager;
2              
3 1     1   26144 use 5.008008;
  1         7  
  1         42  
4 1     1   5 use strict;
  1         2  
  1         40  
5 1     1   4 use warnings;
  1         6  
  1         33  
6              
7 1     1   242056 use CGI;
  1         22105  
  1         19  
8 1     1   1080 use JSON::XS;
  1         6250  
  1         72  
9 1     1   553 use Net::SSH2;
  0            
  0            
10             use Net::SSH2::SFTP;
11             use Imager;
12             use Cwd 'abs_path';
13             use Digest::MD5 qw(md5_hex);
14             use URI;
15              
16             #use LWP::UserAgent;
17             #use LWP::Protocol::https;
18              
19             our $VERSION = '1.01';
20              
21             my %errors = (
22             '_validate_max_file_size' => 'File is too big',
23             '_validate_min_file_size' => 'File is too small',
24             '_validate_accept_file_types' => 'Filetype not allowed',
25             '_validate_max_number_of_files' => 'Maximum number of files exceeded',
26             '_validate_max_width' => 'Image exceeds maximum width',
27             '_validate_min_width' => 'Image requires a minimum width',
28             '_validate_max_height' => 'Image exceeds maximum height',
29             '_validate_min_height' => 'Image requires a minimum height'
30             );
31              
32             #GETTERS/SETTERS
33             sub new {
34             my $invocant = shift;
35             my $class = ref($invocant) || $invocant;
36             my $self = {
37             field_name => 'files[]',
38             ctx => undef,
39             cgi => undef,
40             thumbnail_width => 80,
41             thumbnail_height => 80,
42             thumbnail_quality => 70,
43             thumbnail_format => 'jpg',
44             thumbnail_density => undef,
45             format => 'jpg',
46             quality => 70,
47              
48             thumbnail_filename => undef,
49             thumbnail_prefix => 'thumb_',
50             thumbnail_postfix => '',
51             filename => undef,
52             client_filename => undef,
53             show_client_filename => 1,
54             use_client_filename => undef,
55             filename_salt => '',
56             script_url => undef,
57             tmp_dir => '/tmp',
58             should_delete => 1,
59              
60             absolute_filename => undef,
61             absolute_thumbnail_filename => undef,
62              
63             delete_params => [],
64              
65             upload_dir => undef,
66             thumbnail_upload_dir => undef,
67             upload_url_base => undef,
68             thumbnail_url_base => undef,
69             relative_url_path => '/files',
70             thumbnail_relative_url_path => undef,
71             relative_to_host => undef,
72             delete_url => undef,
73              
74             data => {},
75              
76             #callbacks
77             post_delete => sub {},
78             post_post => sub {},
79             post_get => sub {},
80              
81             #pre calls
82             pre_delete => sub {},
83             pre_post => sub {},
84             pre_get => sub {},
85              
86             #scp/rcp login info
87             scp => [],
88              
89             #user validation specifications
90             max_file_size => undef,
91             min_file_size => 1,
92             accept_file_types => [],
93             require_image => undef,
94             max_width => undef,
95             max_height => undef,
96             min_width => 1,
97             min_height => 1,
98             max_number_of_files => undef,
99              
100             #not to be used by users
101             output => undef,
102             handle => undef,
103             tmp_filename => undef,
104             fh => undef,
105             error => undef,
106             upload => undef,
107             file_type => undef,
108             is_image => undef,
109             imager => undef,
110             width => undef,
111             height => undef,
112             num_files_in_dir => undef,
113             @_, # Override previous attributes
114             };
115             return bless $self, $class;
116             }
117              
118             sub upload_dir {
119             my $self = shift;
120              
121             if (@_) {
122             $self->{upload_dir} = shift;
123             }
124              
125             #set upload_dir to directory of this script if not provided
126             if(!(defined $self->{upload_dir})) {
127             $self->{upload_dir} = abs_path($0);
128             $self->{upload_dir} =~ s/(.*)\/.*/$1/;
129             $self->{upload_dir} .= '/files';
130             }
131              
132             return $self->{upload_dir};
133             }
134              
135             sub thumbnail_upload_dir {
136             my $self = shift;
137              
138             if (@_) {
139             $self->{thumbnail_upload_dir} = shift;
140             }
141              
142             #set upload_dir to directory of this script if not provided
143             if(!(defined $self->{thumbnail_upload_dir})) {
144             $self->{thumbnail_upload_dir} = $self->upload_dir;
145             }
146              
147             return $self->{thumbnail_upload_dir};
148             }
149              
150             sub upload_url_base {
151             my $self = shift;
152              
153             if (@_) {
154             $self->{upload_url_base} = shift;
155             }
156              
157             if(!(defined $self->{upload_url_base})) {
158             $self->{upload_url_base} = $self->_url_base . $self->relative_url_path;
159             }
160              
161             return $self->{upload_url_base};
162             }
163              
164             sub _url_base {
165             my $self = shift;
166             my $url;
167              
168             if($self->relative_to_host) {
169             $url = $self->{uri}->scheme . '://' . $self->{uri}->host;
170             }
171             else {
172             $url = $self->script_url;
173             $url =~ s/(.*)\/.*/$1/;
174             }
175              
176             return $url;
177             }
178              
179             sub thumbnail_url_base {
180             my $self = shift;
181              
182             if (@_) {
183             $self->{thumbnail_url_base} = shift;
184             }
185              
186             if(!(defined $self->{thumbnail_url_base})) {
187             if(defined $self->thumbnail_relative_url_path) {
188             $self->{thumbnail_url_base} = $self->_url_base . $self->thumbnail_relative_url_path;
189             }
190             else {
191             $self->{thumbnail_url_base} = $self->upload_url_base;
192             }
193             }
194              
195             return $self->{thumbnail_url_base};
196             }
197              
198              
199             sub relative_url_path {
200             my $self = shift;
201              
202             if(@_) {
203             $self->{relative_url_path} = shift;
204             }
205              
206             return $self->{relative_url_path};
207             }
208              
209             sub thumbnail_relative_url_path {
210             my $self = shift;
211              
212             if(@_) {
213             $self->{thumbnail_relative_url_path} = shift;
214             }
215              
216             return $self->{thumbnail_relative_url_path};
217             }
218              
219             sub relative_to_host {
220             my $self = shift;
221              
222             if(@_) {
223             $self->{relative_to_host} = shift;
224             }
225              
226             return $self->{relative_to_host};
227             }
228              
229              
230              
231             sub field_name {
232             my $self = shift;
233              
234             if (@_) {
235             $self->{field_name} = shift;
236             }
237              
238             return $self->{field_name};
239             }
240              
241             sub ctx {
242             my $self = shift;
243              
244             if (@_) {
245             $self->{ctx} = shift;
246             }
247              
248             return $self->{ctx};
249             }
250              
251             sub cgi {
252             my $self = shift;
253              
254             if (@_) {
255             $self->{cgi} = shift;
256             }
257             $self->{cgi} = CGI->new unless defined $self->{cgi};
258              
259             return $self->{cgi};
260             }
261              
262             sub should_delete {
263             my $self = shift;
264              
265             if (@_) {
266             $self->{should_delete} = shift;
267             }
268              
269             return $self->{should_delete};
270             }
271              
272             sub scp {
273             my $self = shift;
274              
275             if (@_) {
276             $self->{scp} = shift;
277             }
278              
279             return $self->{scp};
280             }
281              
282             sub max_file_size {
283             my $self = shift;
284              
285             if (@_) {
286             $self->{max_file_size} = shift;
287             }
288              
289             return $self->{max_file_size};
290             }
291              
292             sub min_file_size {
293             my $self = shift;
294              
295             if (@_) {
296             $self->{min_file_size} = shift;
297             }
298              
299             return $self->{min_file_size};
300             }
301              
302             sub accept_file_types {
303             my $self = shift;
304              
305             if (@_) {
306             my $a_ref = shift;
307             die "accept_file_types must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY');
308             $self->{accept_file_types} = $a_ref;
309             }
310              
311             if(scalar(@{$self->{accept_file_types}}) == 0 and $self->require_image) {
312             $self->{accept_file_types} = ['image/jpeg','image/jpg','image/png','image/gif'];
313             }
314              
315             return $self->{accept_file_types};
316             }
317              
318             sub require_image {
319             my $self = shift;
320              
321             if (@_) {
322             $self->{require_image} = shift;
323             }
324              
325             return $self->{require_image};
326             }
327              
328             sub delete_params {
329             my $self = shift;
330              
331             if (@_) {
332             my $a_ref = shift;
333             die "delete_params must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY');
334             $self->{delete_params} = $a_ref;
335             }
336              
337             return $self->{delete_params};
338             }
339              
340             sub delete_url {
341             my $self = shift;
342              
343             if(@_) {
344             $self->{delete_url} = shift;
345             }
346              
347             return $self->{delete_url};
348             }
349              
350             sub thumbnail_width {
351             my $self = shift;
352              
353             if (@_) {
354             $self->{thumbnail_width} = shift;
355             }
356              
357             return $self->{thumbnail_width};
358             }
359              
360             sub thumbnail_height {
361             my $self = shift;
362              
363             if (@_) {
364             $self->{thumbnail_height} = shift;
365             }
366              
367             return $self->{thumbnail_height};
368             }
369              
370             sub thumbnail_quality {
371             my $self = shift;
372              
373             if (@_) {
374             $self->{thumbnail_quality} = shift;
375             }
376              
377             return $self->{thumbnail_quality};
378             }
379              
380             sub thumbnail_format {
381             my $self = shift;
382              
383             if (@_) {
384             $self->{thumbnail_format} = shift;
385             }
386              
387             return $self->{thumbnail_format};
388             }
389              
390             sub thumbnail_density {
391             my $self = shift;
392              
393             if (@_) {
394             $self->{thumbnail_density} = shift;
395             }
396              
397             return $self->{thumbnail_density};
398             }
399              
400             sub thumbnail_prefix {
401             my $self = shift;
402              
403             if (@_) {
404             $self->{thumbnail_prefix} = shift;
405             }
406              
407             return $self->{thumbnail_prefix};
408             }
409              
410             sub thumbnail_postfix {
411             my $self = shift;
412              
413             if (@_) {
414             $self->{thumbnail_postfix} = shift;
415             }
416              
417             return $self->{thumbnail_postfix};
418             }
419              
420             sub thumbnail_final_width {
421             my $self = shift;
422              
423             if(@_) {
424             $self->{thumbnail_final_width} = shift;
425             }
426              
427             return $self->{thumbnail_final_width};
428             }
429              
430             sub thumbnail_final_height {
431             my $self = shift;
432              
433             if(@_) {
434             $self->{thumbnail_final_height} = shift;
435             }
436              
437             return $self->{thumbnail_final_height};
438             }
439              
440             sub quality {
441             my $self = shift;
442              
443             if (@_) {
444             $self->{quality} = shift;
445             }
446              
447             return $self->{quality};
448             }
449              
450             sub format {
451             my $self = shift;
452              
453             if (@_) {
454             $self->{format} = shift;
455             }
456              
457             return $self->{format};
458             }
459              
460             sub final_width {
461             my $self = shift;
462              
463             if(@_) {
464             $self->{final_width} = shift;
465             }
466              
467             return $self->{final_width};
468             }
469              
470             sub final_height {
471             my $self = shift;
472              
473             if(@_) {
474             $self->{final_height} = shift;
475             }
476              
477             return $self->{final_height};
478             }
479              
480             sub max_width {
481             my $self = shift;
482              
483             if (@_) {
484             $self->{max_width} = shift;
485             }
486              
487             return $self->{max_width};
488             }
489              
490             sub max_height {
491             my $self = shift;
492              
493             if (@_) {
494             $self->{max_height} = shift;
495             }
496              
497             return $self->{max_height};
498             }
499              
500             sub min_width {
501             my $self = shift;
502              
503             if (@_) {
504             $self->{min_width} = shift;
505             }
506              
507             return $self->{min_width};
508             }
509              
510             sub min_height {
511             my $self = shift;
512              
513             if (@_) {
514             $self->{min_height} = shift;
515             }
516              
517             return $self->{min_height};
518             }
519              
520             sub max_number_of_files {
521             my $self = shift;
522              
523             if (@_) {
524             $self->{max_number_of_files} = shift;
525             }
526              
527             return $self->{max_number_of_files};
528             }
529              
530             sub filename {
531             my $self = shift;
532              
533             if (@_) {
534             $self->{filename} = shift;
535             }
536              
537             return $self->{filename};
538             }
539              
540             sub absolute_filename {
541             my $self = shift;
542              
543             if (@_) {
544             $self->{absolute_filename} = shift;
545             }
546              
547             return $self->{absolute_filename};
548             }
549              
550             sub thumbnail_filename {
551             my $self = shift;
552              
553             if (@_) {
554             $self->{thumbnail_filename} = shift;
555             }
556              
557             return $self->{thumbnail_filename};
558             }
559              
560             sub absolute_thumbnail_filename {
561             my $self = shift;
562              
563             if (@_) {
564             $self->{absolute_thumbnail_filename} = shift;
565             }
566              
567             return $self->{absolute_thumbnail_filename};
568             }
569              
570             sub client_filename {
571             my $self = shift;
572              
573             if (@_) {
574             $self->{client_filename} = shift;
575             }
576              
577             return $self->{client_filename};
578             }
579              
580             sub show_client_filename {
581             my $self = shift;
582              
583             if (@_) {
584             $self->{show_client_filename} = shift;
585             }
586              
587             return $self->{show_client_filename};
588             }
589              
590             sub use_client_filename {
591             my $self = shift;
592              
593             if (@_) {
594             $self->{use_client_filename} = shift;
595             }
596              
597             return $self->{use_client_filename};
598             }
599              
600             sub filename_salt {
601             my $self = shift;
602              
603             if (@_) {
604             $self->{filename_salt} = shift;
605             }
606              
607             return $self->{filename_salt};
608             }
609              
610             sub tmp_dir {
611             my $self = shift;
612              
613             if (@_) {
614             $self->{tmp_dir} = shift;
615             }
616              
617             return $self->{tmp_dir};
618             }
619              
620             sub script_url {
621             my $self = shift;
622              
623             if (@_) {
624             $self->{script_url} = shift;
625             }
626              
627             if(!(defined $self->{script_url})) {
628             if(defined $self->ctx) {
629             $self->{script_url} = $self->ctx->request->uri;
630             }
631             else {
632             $self->{script_url} = $ENV{SCRIPT_URI};
633             }
634             }
635              
636             return $self->{script_url};
637             }
638              
639             sub data {
640             my $self = shift;
641              
642             if(@_) {
643             $self->{data} = shift;
644             }
645              
646             return $self->{data};
647             }
648              
649             #GETTERS
650             sub output { shift->{output} }
651             sub url { shift->{url} }
652             sub thumbnail_url { shift->{thumbnail_url} }
653             sub is_image { shift->{is_image} }
654              
655             #OTHER METHODS
656             sub print_response {
657             my $self = shift;
658              
659             my $content_type = 'text/plain';
660             if(defined $self->ctx) {
661              
662             #thanks to Lukas Rampa for this suggestion
663             if ($self->ctx->req->headers->header('Accept') =~ qr(application/json) ) {
664             $content_type = 'application/json';
665             }
666              
667             $self->ctx->stash->{current_view} = '';
668             $self->ctx->res->content_type("$content_type; charset=utf-8");
669             $self->ctx->res->body($self->output . ""); #concatenate "" for when there is no output
670             }
671             else {
672             print "Content-type: $content_type\n\n";
673             print $self->output;
674             }
675             }
676              
677             sub handle_request {
678             my $self = shift;
679             my ($print) = @_;
680             my $method = $self->_get_request_method;
681              
682             if($method eq 'GET') {
683             &{$self->pre_get}($self);
684             &{$self->post_get}($self);
685             }
686             elsif($method eq 'PATCH' or $method eq 'POST' or $method eq 'PUT') {
687             &{$self->pre_post}($self);
688             $self->_post;
689             &{$self->post_post}($self);
690             }
691             elsif($method eq 'DELETE') {
692             &{$self->pre_delete}($self); #even though we may not delete, we should give user option to still run code
693             if($self->should_delete) {
694             $self->_delete;
695             &{$self->post_delete}($self);
696             }
697             }
698             else {
699             $self->_set_status(405);
700             }
701              
702             $self->print_response if $print;
703             $self->_clear;
704             }
705              
706             sub generate_output {
707             my $self = shift;
708             my ($arr_ref) = @_;
709              
710             #necessary if we are going to use _url_base via thumbnail_url_base and upload_url_base
711             $self->_set_uri;
712              
713             my @arr;
714             for(@$arr_ref) {
715             my %h;
716             die "Must provide a filename in generate_output" unless exists $_->{filename};
717             die "Must provide a size in generate_output" unless exists $_->{size};
718             $self->{is_image} = $_->{image} eq 'y' ? 1 : 0;
719             $h{size} = $_->{size};
720             $h{error} = $_->{error};
721              
722             if(exists $_->{'name'}) {
723             $h{name} = $_->{name}
724             }
725             else {
726             $h{name} = $_->{filename};
727             }
728              
729             if(exists $_->{thumbnail_filename}) {
730             $self->thumbnail_filename($_->{thumbnail_filename});
731             }
732             else {
733             my $no_ext = $self->_no_ext;
734             $self->thumbnail_filename($self->thumbnail_prefix . $no_ext . $self->thumbnail_postfix . '.' . $self->thumbnail_format);
735             }
736              
737             $self->_set_urls;
738             $h{url} = $_->{url} eq '' ? $self->url : $_->{url};
739             $h{thumbnail_url} = $_->{thumbnail_url} eq '' ? $self->thumbnail_url : $_->{thumbnail_url};
740              
741             $h{delete_url} = $_->{'delete_url'} eq '' ? $self->_delete_url($_->{delete_params}) : $_->{'delete_url'};
742             $h{delete_type} = 'DELETE';
743             push @arr, \%h;
744              
745             #reset for the next time around
746             $self->delete_url('');
747             }
748              
749             #they should provide image=y or image=n if image
750             my $json = JSON::XS->new->ascii->pretty->allow_nonref;
751             $self->{output} = $json->encode({files => \@arr});
752             }
753              
754             sub _no_ext {
755             my $self = shift;
756             $self->filename($_->{filename});
757             my ($no_ext) = $self->filename =~ /(.*)\.(.*)/;
758             return $no_ext;
759             }
760              
761             #PRE/POST METHODS
762             sub pre_delete {
763             my $self = shift;
764              
765             if (@_) {
766             $self->{pre_delete} = shift;
767             }
768              
769             return $self->{pre_delete};
770             }
771              
772             sub post_delete {
773             my $self = shift;
774              
775             if (@_) {
776             $self->{post_delete} = shift;
777             }
778              
779             return $self->{post_delete};
780             }
781              
782             sub pre_post {
783             my $self = shift;
784              
785             if (@_) {
786             $self->{pre_post} = shift;
787             }
788              
789             return $self->{pre_post};
790             }
791              
792             sub post_post {
793             my $self = shift;
794              
795             if (@_) {
796             $self->{post_post} = shift;
797             }
798              
799             return $self->{post_post};
800             }
801              
802             sub pre_get {
803             my $self = shift;
804              
805             if (@_) {
806             $self->{pre_get} = shift;
807             }
808              
809             return $self->{pre_get};
810             }
811              
812             sub post_get {
813             my $self = shift;
814              
815             if (@_) {
816             $self->{post_get} = shift;
817             }
818              
819             return $self->{post_get};
820             }
821              
822             sub _clear {
823             my $self = shift;
824              
825             #clear cgi object so we get a new one for new request
826             $self->{cgi} = undef;
827             $self->{handle} = undef;
828             $self->{tmp_filename} = undef;
829             $self->{upload} = undef;
830             $self->{fh} = undef;
831             $self->{file_size} = undef;
832             $self->{error} = undef;
833             $self->{file_type} = undef;
834             $self->{is_image} = 0;
835             $self->{width} = undef;
836             $self->{height} = undef;
837             $self->{num_files_in_dir} = undef;
838             $self->{output} = undef;
839             $self->{client_filename} = undef;
840             $self->{tmp_thumb_path} = undef;
841             $self->{tmp_file_path} = undef;
842             }
843              
844             sub _post {
845             my $self = shift;
846              
847             if($self->_prepare_file_attrs and $self->_validate_file) {
848             if($self->is_image) {
849             $self->_create_thumbnail;
850             $self->_create_tmp_image
851             }
852             $self->_save;
853             }
854              
855             #delete temporary files
856             if($self->is_image) {
857             unlink ($self->{tmp_thumb_path}, $self->{tmp_file_path});
858             }
859              
860             #generate json output
861             $self->_generate_output;
862             }
863              
864             sub _generate_output {
865             my $self = shift;
866              
867             my %hash;
868             $hash{'name'} = $self->show_client_filename ? $self->client_filename . "" : $self->filename;
869             $hash{'size'} = $self->{file_size};
870             $hash{'url'} = $self->url;
871             $hash{'thumbnail_url'} = $self->thumbnail_url;
872             $hash{'delete_url'} = $self->_delete_url;
873             $hash{'delete_type'} = 'DELETE';
874              
875             $hash{'error'} = $self->_generate_error;
876              
877             my $json = JSON::XS->new->ascii->pretty->allow_nonref;
878             $self->{output} = $json->encode({files => [\%hash]});
879             }
880              
881             sub _delete {
882             my $self = shift;
883              
884             my $filename = $self->_get_param('filename');
885             my $thumbnail_filename = $self->_get_param('thumbnail_filename');
886             my $image_yn = $self->_get_param('image');
887              
888             if(@{$self->scp}) {
889             for(@{$self->scp}) {
890              
891             my $ssh2 = $self->_auth_user($_);
892             $_->{thumbnail_upload_dir} = $_->{upload_dir} if $_->{thumbnail_upload_dir} eq '';
893              
894             my $sftp = $ssh2->sftp;
895             $sftp->unlink($_->{upload_dir} . '/' . $filename);
896             $sftp->unlink($_->{thumbnail_upload_dir} . '/' . $thumbnail_filename) if $image_yn eq 'y';
897             }
898             }
899             else {
900             my $no_ext = $self->_no_ext;
901             unlink $self->upload_dir . '/' . $filename;
902             unlink($self->thumbnail_upload_dir . '/' . $thumbnail_filename) if $image_yn eq 'y';
903             }
904             }
905              
906             sub _get_param {
907             my $self = shift;
908             my ($param) = @_;
909              
910             if(defined $self->ctx) {
911             return $self->ctx->req->params->{$param};
912             }
913             else {
914             return $self->cgi->param($param);
915             }
916             }
917              
918             sub _delete_url {
919             my $self = shift;
920             return if $self->delete_url ne '';
921             my ($delete_params) = @_;
922              
923             my $url = $self->script_url;
924             my $uri = $self->{uri}->clone;
925              
926             my $image_yn = $self->is_image ? 'y' : 'n';
927              
928             unless(defined $delete_params and scalar(@$delete_params)) {
929             $delete_params = [];
930             }
931              
932             push @$delete_params, @{$self->delete_params} if @{$self->delete_params};
933             push @$delete_params, ('filename',$self->filename,'image',$image_yn);
934             push @$delete_params, ('thumbnail_filename',$self->thumbnail_filename) if $self->is_image;
935              
936             $uri->query_form($delete_params);
937              
938             $self->delete_url($uri->as_string);
939              
940             return $self->delete_url;
941             }
942              
943             sub _script_url {
944             my $self = shift;
945              
946             if(defined $self->ctx) {
947             return $self->ctx->request->uri;
948             }
949             else {
950             return $ENV{'SCRIPT_URI'};
951             }
952             }
953              
954             sub _prepare_file_attrs {
955             my $self = shift;
956              
957             #ORDER MATTERS
958             return undef unless $self->_set_upload_obj;
959             $self->_set_fh;
960             $self->_set_file_size;
961             $self->_set_client_filename;
962             $self->_set_tmp_filename;
963             $self->_set_file_type;
964             $self->_set_is_image;
965             $self->_set_filename;
966             $self->_set_absolute_filenames;
967             $self->_set_imager;
968             $self->_set_width;
969             $self->_set_height;
970             $self->_set_num_files_in_dir;
971             $self->_set_uri;
972             $self->_set_urls;
973              
974             return 1;
975             }
976              
977             sub _set_urls {
978             my $self = shift;
979              
980             if($self->is_image) {
981             $self->{thumbnail_url} = $self->thumbnail_url_base . '/' . $self->thumbnail_filename;
982             }
983             $self->{url} = $self->upload_url_base . '/' . $self->filename;
984             }
985              
986             sub _set_uri {
987             my $self = shift;
988             #if catalyst, use URI already made?
989             if(defined $self->ctx) {
990             $self->{uri} = $self->ctx->req->uri;
991             }
992             else {
993             $self->{uri} = URI->new($self->script_url);
994             }
995             }
996              
997             sub _generate_error {
998             my $self = shift;
999             return undef unless defined $self->{error} and @{$self->{error}};
1000              
1001             my $restrictions = join ',', @{$self->{error}->[1]};
1002             return $errors{$self->{error}->[0]} . " Restriction: $restrictions Provided: " . $self->{error}->[2];
1003             }
1004              
1005             sub _validate_file {
1006             my $self = shift;
1007             return undef unless
1008             $self->_validate_max_file_size and
1009             $self->_validate_min_file_size and
1010             $self->_validate_accept_file_types and
1011             $self->_validate_max_width and
1012             $self->_validate_min_width and
1013             $self->_validate_max_height and
1014             $self->_validate_min_height and
1015             $self->_validate_max_number_of_files;
1016              
1017             return 1;
1018             }
1019              
1020             sub _save {
1021             my $self = shift;
1022              
1023             if(@{$self->scp}) {
1024             $self->_save_scp;
1025             }
1026             else {
1027             $self->_save_local;
1028             }
1029             }
1030              
1031             sub _save_scp {
1032             my $self = shift;
1033              
1034             for(@{$self->scp}) {
1035             die "Must provide a host to scp" if $_->{host} eq '';
1036              
1037             $_->{thumbnail_upload_dir} = $_->{upload_dir} if $_->{thumbnail_upload_dir} eq '';
1038              
1039             my $path = $_->{upload_dir} . '/' . $self->filename;
1040             my $thumb_path = $_->{thumbnail_upload_dir} . '/' . $self->thumbnail_filename;
1041              
1042             if(($_->{user} ne '' and $_->{public_key} ne '' and $_->{private_key} ne '') or ($_->{user} ne '' and $_->{password} ne '')) {
1043             my $ssh2 = $self->_auth_user($_);
1044              
1045             #if it is an image, scp both file and thumbnail
1046             if($self->is_image) {
1047             $ssh2->scp_put($self->{tmp_file_path}, $path);
1048             $ssh2->scp_put($self->{tmp_thumb_path}, $thumb_path);
1049             }
1050             else {
1051             $ssh2->scp_put($self->{tmp_filename}, $path);
1052             }
1053              
1054             $ssh2->disconnect;
1055             }
1056             else {
1057             die "Must provide a user and password or user and identity file for connecting to host";
1058             }
1059              
1060             }
1061             }
1062              
1063             sub _auth_user {
1064             my $self = shift;
1065             my ($auth) = @_;
1066              
1067             my $ssh2 = Net::SSH2->new;
1068              
1069             $ssh2->connect($auth->{host}) or die $!;
1070              
1071             #authenticate
1072             if($auth->{user} ne '' and $auth->{public_key} ne '' and $auth->{private_key} ne '') {
1073             $ssh2->auth_publickey($auth->{user},$auth->{public_key},$auth->{private_key});
1074             }
1075             else {
1076             $ssh2->auth_password($auth->{user},$auth->{password});
1077             }
1078              
1079             unless($ssh2->auth_ok) {
1080             die "error authenticating with remote server";
1081             }
1082              
1083             die "upload directory must be provided with scp hash" if $auth->{upload_dir} eq '';
1084              
1085             return $ssh2;
1086             }
1087              
1088             sub _save_local {
1089             my $self = shift;
1090              
1091             #if image
1092             if($self->is_image) {
1093             rename $self->{tmp_file_path}, $self->absolute_filename;
1094             rename $self->{tmp_thumb_path}, $self->absolute_thumbnail_filename;
1095             }
1096             #if non-image with catalyst
1097             elsif(defined $self->ctx) {
1098             $self->{upload}->link_to($self->absolute_filename);
1099             }
1100             #if non-image with regular CGI perl
1101             else {
1102             my $io_handle = $self->{fh}->handle;
1103              
1104             my $buffer;
1105             open (OUTFILE,'>', $self->absolute_filename);
1106             while (my $bytesread = $io_handle->read($buffer,1024)) {
1107             print OUTFILE $buffer;
1108             }
1109             close OUTFILE;
1110             }
1111             }
1112              
1113             sub _validate_max_file_size {
1114             my $self = shift;
1115             return 1 unless $self->max_file_size;
1116              
1117             if($self->{file_size} > $self->max_file_size) {
1118             $self->{error} = ['_validate_max_file_size',[$self->max_file_size],$self->{file_size}];
1119             return undef;
1120             }
1121             else {
1122             return 1;
1123             }
1124             }
1125              
1126             sub _validate_min_file_size {
1127             my $self = shift;
1128             return 1 unless $self->min_file_size;
1129              
1130             if($self->{file_size} < $self->min_file_size) {
1131             $self->{error} = ['_validate_min_file_size',[$self->min_file_size],$self->{file_size}];
1132             return undef;
1133             }
1134             else {
1135             return 1;
1136             }
1137             }
1138              
1139             sub _validate_accept_file_types {
1140             my $self = shift;
1141              
1142             #if accept_file_types is empty, we except all types
1143             #so return true
1144             return 1 unless @{$self->accept_file_types};
1145              
1146             if(grep { $_ eq $self->{file_type} } @{$self->{accept_file_types}}) {
1147             return 1;
1148             }
1149             else {
1150             my $types = join ",", @{$self->accept_file_types};
1151             $self->{error} = ['_validate_accept_file_types',[$types],$self->{file_type}];
1152             return undef;
1153             }
1154             }
1155              
1156             sub _validate_max_width {
1157             my $self = shift;
1158             return 1 unless $self->is_image;
1159              
1160             #if set to undef, there's no max_width
1161             return 1 unless $self->max_width;
1162              
1163             if($self->{width} > $self->max_width) {
1164             $self->{error} = ['_validate_max_width',[$self->max_width],$self->{width}];
1165             return undef;
1166             }
1167             else {
1168             return 1;
1169             }
1170             }
1171              
1172             sub _validate_min_width {
1173             my $self = shift;
1174             return 1 unless $self->is_image;
1175              
1176             #if set to undef, there's no min_width
1177             return 1 unless $self->min_width;
1178              
1179             if($self->{width} < $self->min_width) {
1180             $self->{error} = ['_validate_min_width',[$self->min_width],$self->{width}];
1181             return undef;
1182             }
1183             else {
1184             return 1;
1185             }
1186             }
1187              
1188             sub _validate_max_height {
1189             my $self = shift;
1190             return 1 unless $self->is_image;
1191              
1192             #if set to undef, there's no max_height
1193             return 1 unless $self->max_height;
1194              
1195             if($self->{height} > $self->max_height) {
1196             $self->{error} = ['_validate_max_height',[$self->max_height],$self->{height}];
1197             return undef;
1198             }
1199             else {
1200             return 1;
1201             }
1202             }
1203              
1204             sub _validate_min_height {
1205             my $self = shift;
1206             return 1 unless $self->is_image;
1207              
1208             #if set to undef, there's no max_height
1209             return 1 unless $self->min_height;
1210              
1211             if($self->{height} < $self->min_height) {
1212             $self->{error} = ['_validate_min_height',[$self->min_height],$self->{height}];
1213             return undef;
1214             }
1215             else {
1216             return 1;
1217             }
1218             }
1219              
1220             sub _validate_max_number_of_files {
1221             my $self = shift;
1222             return 1 unless $self->max_number_of_files;
1223              
1224             if($self->{num_files_in_dir} > $self->max_number_of_files) {
1225             $self->{error} = ['_validate_max_number_of_files',[$self->max_number_of_files],$self->{num_files_in_dir}];
1226             return undef;
1227             }
1228             else {
1229             return 1;
1230             }
1231             }
1232              
1233             sub _set_file_size {
1234             my $self = shift;
1235              
1236             if(defined $self->ctx) {
1237             $self->{file_size} = $self->{upload}->size;
1238             }
1239             else {
1240             $self->{file_size} = -s $self->{upload};
1241             }
1242              
1243             return $self->{file_size};
1244             }
1245              
1246             sub _set_client_filename {
1247             my $self = shift;
1248             return if defined $self->client_filename;
1249              
1250             if(defined $self->ctx) {
1251             $self->client_filename($self->{upload}->filename);
1252             }
1253             else {
1254             $self->client_filename($self->cgi->param($self->field_name));
1255             }
1256              
1257             return $self->client_filename;
1258             }
1259              
1260             sub _set_filename {
1261             my $self = shift;
1262             return if defined $self->filename;
1263              
1264             if($self->use_client_filename) {
1265             $self->filename($self->client_filename);
1266             }
1267             else {
1268             my $filename = md5_hex($self->client_filename . time() . int(rand(1000))) . time() . $self->filename_salt;
1269             $self->thumbnail_filename($self->thumbnail_prefix . $filename . $self->thumbnail_postfix . '.' . $self->thumbnail_format) unless $self->thumbnail_filename;
1270              
1271             if($self->is_image) {
1272             $filename .= '.' . $self->format;
1273             }
1274             else {
1275             #add extension if present
1276             if($self->client_filename =~ /.*\.(.*)/) {
1277             $filename .= '.' . $1;
1278             }
1279             }
1280             $self->filename($filename) unless $self->filename;
1281             }
1282              
1283             return $self->filename;
1284             }
1285              
1286             sub _set_absolute_filenames {
1287             my $self = shift;
1288              
1289             $self->absolute_filename($self->upload_dir . '/' . $self->filename) unless $self->absolute_filename;
1290             $self->absolute_thumbnail_filename($self->thumbnail_upload_dir . '/' . $self->thumbnail_filename) unless $self->absolute_thumbnail_filename;
1291             }
1292              
1293             sub _set_file_type {
1294             my $self = shift;
1295              
1296             if(defined $self->ctx) {
1297             $self->{file_type} = $self->{upload}->type;
1298             }
1299             else {
1300             $self->{file_type} = $self->cgi->uploadInfo($self->client_filename)->{'Content-Type'};
1301             }
1302              
1303             return $self->{file_type};
1304             }
1305              
1306             sub _set_is_image {
1307             my $self = shift;
1308              
1309             if($self->{file_type} eq 'image/jpeg' or $self->{file_type} eq 'image/jpg' or $self->{file_type} eq 'image/png' or $self->{file_type} eq 'image/gif') {
1310             $self->{is_image} = 1;
1311             }
1312             else {
1313             $self->{is_image} = 0;
1314             }
1315              
1316             return $self->is_image;
1317             }
1318              
1319             sub _set_imager {
1320             my $self = shift;
1321             return unless $self->is_image;
1322              
1323             #if used in persistent setting, don't recreate object
1324             $self->{imager} = Imager->new unless defined $self->{imager};
1325              
1326             $self->{imager}->read(fh => $self->{fh});
1327              
1328             return $self->{imager};
1329             }
1330              
1331             sub _set_width {
1332             my $self = shift;
1333             return unless $self->is_image;
1334              
1335             $self->{width} = $self->{imager}->getwidth;
1336             }
1337              
1338             sub _set_height {
1339             my $self = shift;
1340             return unless $self->is_image;
1341              
1342             $self->{height} = $self->{imager}->getheight;
1343             }
1344              
1345             sub _set_tmp_filename {
1346             my $self = shift;
1347              
1348             my $tmp_filename;
1349             if(defined $self->ctx) {
1350             $self->{tmp_filename} = $self->{upload}->tempname;
1351             }
1352             else {
1353             $self->{tmp_filename} = $self->cgi->tmpFileName($self->client_filename);
1354             }
1355             }
1356              
1357             sub _set_upload_obj {
1358             my $self = shift;
1359              
1360             if(defined $self->ctx) {
1361             $self->{upload} = $self->ctx->request->upload($self->field_name);
1362             }
1363             else {
1364             $self->{upload} = $self->cgi->upload($self->field_name);
1365             }
1366              
1367             return defined $self->{upload};
1368             }
1369              
1370             sub _set_fh {
1371             my $self = shift;
1372              
1373             if(defined $self->ctx) {
1374             $self->{fh} = $self->{upload}->fh;
1375             }
1376             else {
1377             $self->{fh} = $self->{upload};
1378             }
1379              
1380             return $self->{fh};
1381             }
1382              
1383             sub _set_num_files_in_dir {
1384             my $self = shift;
1385             return unless $self->max_number_of_files;
1386              
1387             #DO SCP VERSION
1388             if(@{$self->{scp}}) {
1389             my $max = 0;
1390             for(@{$self->{scp}}) {
1391             my $ssh2 = $self->_auth_user($_);
1392             my $chan = $ssh2->channel();
1393             $chan->exec('ls -rt ' . $_->{upload_dir} . ' | wc -l');
1394             my $buffer;
1395             $chan->read($buffer,1024);
1396             ($self->{num_files_in_dir}) = $buffer =~ /(\d+)/;
1397             $max = $self->{num_files_in_dir} if $self->{num_files_in_dir} > $max;
1398             }
1399              
1400             #set to maximum of hosts because we know if one's over that's too many
1401             $self->{num_files_in_dir} = $max;
1402             }
1403             else {
1404             my $dir = $self->upload_dir;
1405             my @files = <$dir/*>;
1406             $self->{num_files_in_dir} = @files;
1407             }
1408              
1409             return $self->{num_files_in_dir};
1410             }
1411              
1412             sub _get_request_method {
1413             my $self = shift;
1414              
1415             my $method = '';
1416             if(defined $self->ctx) {
1417             $method = $self->ctx->req->method;
1418             }
1419             else {
1420             $method = $self->cgi->request_method;
1421             }
1422              
1423             return $method;
1424             }
1425              
1426             sub _set_status {
1427             my $self = shift;
1428             my ($response) = @_;
1429              
1430             if(defined $self->ctx) {
1431             $self->ctx->response->status($response);
1432             }
1433             else {
1434             print $self->cgi->header(-status=>$response);
1435             }
1436             }
1437              
1438             sub _set_header {
1439             my $self = shift;
1440             my ($key,$val) = @_;
1441              
1442             if(defined $self->ctx) {
1443             $self->ctx->response->header($key => $val);
1444             }
1445             else {
1446             print $self->cgi->header($key,$val);
1447             }
1448             }
1449              
1450             sub _create_thumbnail {
1451             my $self = shift;
1452              
1453             my $im = $self->{image}->copy;
1454              
1455             #thumb is added at beginning of tmp_thumb_path as to not clash with the original image file path
1456             my $output = $self->{tmp_thumb_path} = $self->tmp_dir . '/thumb_' . $self->thumbnail_filename;
1457             my $width = $self->thumbnail_width;
1458             my $height = $self->thumbnail_height;
1459              
1460             my $density = $self->thumbnail_density || $width . "x" . $height;
1461             my $quality = $self->thumbnail_quality;
1462             my $format = $self->thumbnail_format;
1463              
1464             # source image dimensions
1465             my ($o_width, $o_height) = ($im->getwidth,$im->getheight);
1466              
1467             # calculate image dimensions required to fit onto thumbnail
1468             my ($t_width, $t_height, $ratio);
1469             # wider than tall (seems to work...) needs testing
1470             if( $o_width > $o_height ){
1471             $ratio = $o_width / $o_height;
1472             $t_width = $width;
1473             $t_height = $width / $ratio;
1474              
1475             # still won't fit, find the smallest size.
1476             while($t_height > $height){
1477             $t_height -= $ratio;
1478             $t_width -= 1;
1479             }
1480             }
1481             # taller than wide
1482             elsif( $o_height > $o_width ){
1483             $ratio = $o_height / $o_width;
1484             $t_height = $height;
1485             $t_width = $height / $ratio;
1486              
1487             # still won't fit, find the smallest size.
1488             while($t_width > $width){
1489             $t_width -= $ratio;
1490             $t_height -= 1;
1491             }
1492             }
1493             # square (fixed suggested by Philip Munt phil@savvyshopper.net.au)
1494             elsif( $o_width == $o_height){
1495             $ratio = 1;
1496             $t_height = $width;
1497             $t_width = $width;
1498             while (($t_width > $width) or ($t_height > $height)){
1499             $t_width -= 1;
1500             $t_height -= 1;
1501             }
1502             }
1503              
1504             # Create thumbnail
1505             if( defined $im ){
1506             $im->scale( xpixels => $t_width, ypixels => $t_height, type => 'nonprop' );
1507             # $im->Set( quality => $quality );
1508              
1509             my(@options) = (file => $output, type => $format);
1510              
1511             if ( ($format eq 'jpeg') && defined($quality) )
1512             {
1513             push @options, 'jpegquality', $quality;
1514             }
1515              
1516             # $im->Set( density => $density );
1517              
1518             my($x, $y) = split(/x/, $density);
1519              
1520             if (defined($x) && ($x > 0) && defined($y) && ($y > 0) )
1521             {
1522             $im->scale( xpixels => $x, ypixels => $y, type => 'nonprop' );
1523             }
1524              
1525             $self->final_width($t_width);
1526             $self->final_height($t_height);
1527              
1528             $im->write(@options);
1529             }
1530             }
1531              
1532             sub _create_tmp_image {
1533             my $self = shift;
1534             my $im = $self->{image};
1535              
1536             #main_ is added as to not clash with thumbnail tmp path if thumbnail_prefix = '' and they have the same name
1537             my $output = $self->{tmp_file_path} = $self->tmp_dir . '/main_' . $self->filename;
1538             my $quality = $self->thumbnail_quality;
1539             my $format = $self->thumbnail_format;
1540              
1541             if( defined $im ){
1542             # $im->Set( quality => $quality );
1543              
1544             $im->write(file => $output, type => $format);
1545              
1546             $self->final_width($im->getwidth);
1547             $self->final_height($im->getheight);
1548             }
1549             }
1550              
1551             #sub _save_cloud {
1552             # my $self = shift;
1553             # my $io_handle = $self->{fh}->handle;
1554             #
1555             # #IF IS IMAGE, MUST UPLOAD BOTH IMAGES
1556             #
1557             # my $s_contents;
1558             # while (my $bytesread = $io_handle->read($buffer,1024)) {
1559             # print OUTFILE $buffer;
1560             ## }
1561             #
1562             #
1563             ## while()
1564             # {
1565             # $s_contents .= $_;
1566             ## }
1567             #
1568             ## ### we will call this resource whatever comes after the last /
1569             # my $s_resourceName;
1570             #
1571             ## if($param->{'path'} =~ /^.*\/(.*)$/)
1572             # {
1573             # $s_resourceName = $1;
1574             ## }
1575             # else
1576             # {
1577             # return('fail', "could not parse path: $param->{'path'}");
1578             ## }
1579             #
1580             # ### should we pass these vars ... or look them up?
1581             # my $s_user = '';
1582             # my $s_key = '';
1583             ## my $s_cdn_uri ='';
1584             #
1585             # my $ua = LWP::UserAgent->new;
1586             # my $req = HTTP::Request->new(GET => 'https://auth.api.rackspacecloud.com/v1.0');
1587             ## $req->header('X-Auth-User' => $s_user);
1588             # $req->header('X-Auth-Key' => $s_key);
1589             #
1590             ## my $res = $ua->request($req);
1591             #
1592             # if ($res->is_success)
1593             ## {
1594             # my $s_url = $res->header('X-Storage-Url') . "/container/" . $s_resourceName;
1595             #
1596             ## my $reqPUT = HTTP::Request->new(PUT => $s_url);
1597             # $reqPUT->header('X-Auth-Token' => $res->header('X-Auth-Token'));
1598             #
1599             ## $reqPUT->content( $s_contents );
1600             #
1601             # my $resPUT = $ua->request($reqPUT);
1602             ##
1603             # if($resPUT->is_success)
1604             # {
1605             ## my $s_returnURI = $s_cdn_uri . "/" . $s_resourceName;
1606             # return('pass','passed afaict', $s_returnURI);
1607             # }
1608             ## else
1609             # {
1610             # my $s_temp = $resPUT->as_string;
1611             # $s_temp =~ s/'/\\'/g;
1612             ## return('fail',"PUT failed with response:$s_temp")
1613             # }
1614             # }
1615             ## else
1616             # {
1617             # my $s_temp = $res->as_string;
1618             ## $s_temp =~ s/'/\\'/g;
1619             # return('fail',"failed with response:$s_temp")
1620             # }
1621             ## }
1622             # else
1623             # {
1624             ## return("fail","sorry no file found at $param->{'path'}");
1625             # }
1626             #}
1627             #
1628             ##sub _delete_cloud {
1629             # my $self = shift;
1630             # my $request = HTTP::Request->new( 'DELETE', $self->_url,
1631             #Q [ 'X-Auth-Token' => $self->cloudfiles->token ] );
1632             # my $response = $self->cloudfiles->_request($request);
1633             # confess 'Object ' . $self->name . ' not found' if $response->code == 404;
1634             # confess 'Unknown error' if $response->code != 204;
1635             #}
1636              
1637             # Preloaded methods go here.
1638              
1639             1;
1640             __END__