File Coverage

blib/lib/WWW/Comix/Plugin.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 WWW::Comix::Plugin;
2 1     1   4465 use strict;
  1         3  
  1         45  
3 1     1   6 use warnings;
  1         1  
  1         33  
4 1     1   6 use Carp;
  1         2  
  1         88  
5 1     1   6 use English qw( -no_match_vars );
  1         2  
  1         7  
6 1     1   1150 use Path::Class qw( dir file );
  1         53080  
  1         97  
7 1     1   495 use Moose::Policy 'Moose::Policy::FollowPBP';
  0            
  0            
8             use Moose;
9              
10             has its_agent => (
11             lazy => 1,
12             default => \&get_an_agent,
13             predicate => 'has_agent',
14             reader => 'get_its_agent',
15             writer => 'set_agent',
16             );
17              
18             has directory => (
19             is => 'rw',
20             lazy => 1,
21             default => '.',
22             predicate => 'has_directory',
23             );
24              
25             has comic => (
26             is => 'rw',
27             required => 1,
28             );
29              
30             # The real core you need to implement in subclasses
31             sub get_available_ids {
32             croak 'derived classes MUST implement get_available_ids()';
33             }
34              
35             sub id_to_uri {
36             croak 'derived classes MUST implement method id_to_uri()';
37             }
38              
39             sub probe {
40             croak 'derived classes MUST implement method id_to_uri()';
41             }
42              
43             # What comes for free
44              
45             { # Handle plugin-specific configurations
46             my %config_for;
47              
48             sub BUILD {
49             my $self = shift;
50             my $args = shift;
51              
52             $self->probe() if $args->{probe};
53              
54             return;
55             } ## end sub BUILD
56              
57             sub set_config {
58             my $sp = shift;
59             my %cfg = @_ == 1 ? %{$_[0]} : @_;
60             $config_for{$sp->get_name()} = \%cfg;
61             return;
62             }
63              
64             sub get_config {
65             my $sp = shift;
66             my %args = @_;
67              
68             $sp->probe() if $args{probe};
69              
70             my $name = $sp->get_name();
71             my $rval = $config_for{$name}
72             or croak "plugin $name: didn't probe, no configuration";
73              
74             return $rval unless wantarray();
75             return %$rval;
76             }
77              
78             sub get_comics_list {
79             my $sp = shift;
80             my %cfg = $sp->get_config(@_);
81             return keys %cfg;
82             } ## end sub get_comics_list
83              
84             sub is_ok {
85             my $self = shift;
86             my $config = $self->get_config(); # croaks if...
87             return exists $config->{$self->get_comic()};
88             }
89             }
90              
91             sub get_name { # By default, the name is derived from the package
92             (my $name = ref($_[0]) || $_[0])=~ s{.*::}{}mxs;
93             return $name;
94             }
95              
96             sub get_priority { return 100 } # default priority
97              
98             sub get_agent { # This can be called both as class and instance method
99             my $self = shift;
100             return $self->get_its_agent() if ref $self;
101             return $self->get_an_agent();
102             }
103              
104             sub get_http_response {
105             my $self = shift;
106             return $self->get_agent()->get(_get_uri($self, @_));
107             }
108              
109             sub get_current_id { return $_[0]->get_id_iterator()->(); }
110              
111             sub get_id_iterator {
112             my $self = shift;
113             my @ids = $self->get_available_ids();
114             return sub { return shift @ids; };
115             }
116              
117             sub get { return _get(@_)->content(); }
118              
119             sub getstore {
120             my $res = _get(@_);
121             my $self = shift;
122              
123             my $filename = $self->get_filename(@_, response => $res);
124             open my $fh, '>', $filename or croak "open('$filename'): $OS_ERROR";
125             binmode $fh;
126             print {$fh} $res->content();
127             close $fh;
128              
129             return $filename;
130             } ## end sub getstore
131              
132             sub get_filename {
133             my $self = shift;
134             my (%args) = @_;
135              
136             my $filename =
137             exists($args{filename})
138             ? $args{filename}
139             : $self->guess_filename(%args);
140              
141             $filename = $filename->($self, %args) if ref $filename;
142              
143             return $filename if file($filename)->is_absolute();
144              
145             my $directory =
146             exists($args{directory}) ? $args{directory} : $self->get_directory();
147             return dir($directory)->file($filename)->stringify();
148             } ## end sub get_filename
149              
150             sub guess_filename {
151             my $self = shift;
152             my %args = @_;
153              
154             my $response = $args{response}
155             or croak "can't guess a filename without a HTTP::Response";
156              
157             my $filename;
158             if (my $disp = $response->header('Content-Disposition')) {
159             ($filename) = $disp =~ m{
160             ; \s* filename \s* = \s* (
161             " (?: \\. | [^"])* "
162             | [^"][^;]*
163             )
164             }mxs;
165             if (defined $filename) {
166             $filename =~ s/\\(.)/$1/g;
167             $filename =~ s/\A " | " \z//g;
168             }
169             } ## end if (my $disp = $response...
170              
171             if (!defined $filename) {
172             require URI;
173             $filename = URI->new(_get_uri($self, @_))->path();
174             }
175              
176             return $self->normalise_filename(%args, filename => $filename);
177             } ## end sub guess_filename
178              
179             sub normalise_filename {
180             my $self = shift;
181             my %args = @_;
182              
183             my $filename = $args{filename}
184             or croak "can't normalise a filename without a filename";
185              
186             if ($filename !~ m{\. (?: jpe?g | png | gif ) \z}imxs) {
187             my $extension = $self->guess_file_extension(%args);
188             $filename =~ s/\.\w+\z//mxs;
189             $filename .= '.' . lc($extension);
190             }
191             $filename =~ s/jpeg\z/jpg/mxs;
192              
193             require File::Basename;
194             $filename = File::Basename::basename($filename);
195             $filename = 'image-' . time() . rand(0xFFFF) . $filename
196             if substr($filename, 0, 1) eq '.';
197              
198             return $filename;
199             } ## end sub normalise_filename
200              
201             sub guess_file_extension {
202             my $self = shift;
203             my %args = @_;
204              
205             my $response = $args{response}
206             or croak "can't guess file extension without a HTTP::Response";
207              
208             if (my ($type) =
209             $response->header('Content-Type') =~ m{\A image/ ([\w-]+)}mxs)
210             {
211             $type =~ s/jpeg\z/jpg/mxs;
212             return $type;
213             } ## end if (my ($type) = $response...
214              
215             # Taken from WWW::Comic::Plugin::_image_format
216             my $content = $response->content();
217             return 'gif' if $content =~ /\AGIF8[79]a/mxs;
218             return 'jpg' if $content =~ /\A\xFF\xD8/mxs;
219             return 'png' if $content =~ /\A\x89PNG\x0d\x0a\x1a\x0a/mxs;
220              
221             croak q{can't guess type of file, bailing out};
222             } ## end sub guess_file_extension
223              
224             sub get_an_agent {
225             require WWW::Mechanize;
226             my @agents = (
227             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1).',
228             'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) '.
229             'Gecko/20050718 Firefox/1.0.4 (Debian package 1.0.4-2sarge1)',
230             'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.7.5) '.
231             'Gecko/20041110 Firefox/1.0',
232             'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) '.
233             'AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.12',
234             'Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)',
235             );
236             my $agent = WWW::Mechanize->new(
237             agent => $agents[rand @agents],
238             agent => 'BrowsHer/1.2 (winshaw)',
239             autocheck => 1,
240             stack_depth => 2,
241             timeout => 20,
242             );
243             $agent->env_proxy();
244             return $agent;
245             } ## end sub get_an_agent
246              
247             sub _get {
248             my $self = shift;
249             my $response = $self->get_http_response(@_);
250             return $response if $response->is_success();
251              
252             my $uri = _get_uri($self, @_); # for croaking below
253             croak "error getting '$uri': ", $response->status_line();
254             } ## end sub _get
255              
256             sub _get_uri {
257             my ($self, %args) = @_;
258             my $uri =
259             exists($args{id})
260             ? $self->id_to_uri($args{id})
261             : $self->id_to_uri($self->get_current_id());
262              
263             return $uri;
264             } ## end sub _get_uri
265              
266             1; # Magic true value required at end of module
267             __END__
268              
269             =head1 NAME
270              
271             WWW::Comix::Plugin - base class for plugins in WWW::Comix
272              
273             =head1 SYNOPSIS
274              
275             use WWW::Comix;
276              
277             # The constructor for WWW::Comix returns a plugin instance actually
278             my $comix = WWW::Comix->new(
279             comic => 'whatever',
280             probe => 'ok',
281             directory => '/path/to/repository',
282             );
283              
284             # Get current strip and save as 'current.jpg'
285             $comix->getstore(filename => 'current.jpg');
286              
287             # Iterate over available strips, getting image data
288             my $iterator = $comix->get_iterator();
289             while (my $id = $iterator->()) {
290             my $blob = $comix->get(id => $id);
291             }
292              
293             =head1 DESCRIPTION
294              
295             This is the real workhorse for WWW::Comix, encapsulating all the logic and
296             leaving the derived classes to implement only three mandatory methods.
297              
298             This module, and the plugins shipped with it, is proudly based on
299             L<Moose>.
300              
301             =head1 INTERFACE
302              
303             You will not need to call a constructor directly, use L<WWW::Comix> to
304             get a new object. If you already know the name of the plugin you want to
305             use, just pass it to the L<WWW::Comix/new> method:
306              
307             my $comix = WWW::Comix->new(plugin => $plugin, ...);
308              
309             The available methods are divided by functionality. Anyway, the methods
310             that you're likely to need are the following:
311              
312             =over
313              
314             =item *
315              
316             L</get_comics_list>
317              
318             =item *
319              
320             L</get_available_ids> or L</get_id_iterator>
321              
322             =item *
323              
324             L</get> or L</getstore>
325              
326             =back
327              
328             All the other methods aren't likely to satisfy any need of yours.
329              
330             =head2 Accessors
331              
332             =over
333              
334             =item B<< get_agent >>
335              
336             this method can be called either as a class or as an object method. In
337             the former case, it will give back the return value from L</get_an_agent>,
338             in the latter it will give back the return value from L</get_its_agent>.
339              
340             =item B<< get_an_agent >>
341              
342             get a L<WWW::Mechanize> user agent. This method is used to initialise
343             the C<agent> member (see below).
344              
345             =item B<< has_agent >>
346              
347             =item B<< set_agent >>
348              
349             =item B<< get_its_agent >>
350              
351             each plugin has an agent that will be used for actual WWW interactions. It
352             must support the same interface as L<WWW::Mechanize> (so it will either be
353             a L<WWW::Mechanize> object, or an object of some derived class).
354              
355             By default gets an agent invoking L</get_an_agent>. You can tell if an
356             agent has already been set via the C<has_agent> method; the other two
357             methods are the normal setter/getter.
358              
359             =item B<< has_directory >>
360              
361             =item B<< set_directory >>
362              
363             =item B<< get_directory >>
364              
365             the L</getstore> method (see below) will save files inside a directory.
366              
367             You can tell if a directory has been set with the C<has_directory> predicate
368             method. When needed, by default it will be set to C<.>.
369              
370             =item B<< set_status >>
371              
372             =item B<< is_ok >>
373              
374             this holds the (boolean) status of the plugin. Each plugin can have its
375             idea of what a "wrong" status is.
376              
377             =item B<< set_comic >>
378              
379             =item B<< get_comic >>
380              
381             the comic that this plugin will deal with. You are obliged to pass at least
382             a comic name during the construction, but you can change your mind later.
383             Note that if you change the comic name to some unsupported comic Bad Things
384             can happen.
385              
386             =back
387              
388             =head2 Information Related
389              
390             =over
391              
392             =item B<< get_name >>
393              
394             get plugin's name.
395              
396             =item B<< get_priority >>
397              
398             get plugin's priority.
399              
400             The priority is (or can be) used to establish the best provider for any
401             given comic. The lower the value, the higher the priority.
402              
403             =item B<< get_comics_list >>
404              
405             get the list of comics provided by the plugin.
406              
407             Returns the list of available comics; it has no parameter.
408              
409             =item B<< get_available_ids >>
410              
411             get the list of available ids for the configured comic (see
412             L</set_comic> and L</get_comic>).
413              
414             Returns the list of available valid ids for the given plugin/comic; it
415             has no parameter.
416              
417             Note that this method is always overriden by the specific plugin.
418              
419             =item B<< get_id_iterator >>
420              
421             get an iterator to cycle on all the available ids for the comic.
422              
423             The iterator is a C<sub> that can be called without parameters to get the
424             next item in the list. Returns C<undef> once the list is exhausted.
425              
426             It has no parameters.
427              
428             =item B<< get_current_id >>
429              
430             get the id of the latest available strip. This is the same as the first
431             item in the list returned by L</get_available_ids>, or the item returned
432             by the first invocation of an iterator taken with L</get_id_iterator>.
433              
434             Returns a valid identifier for the given plugin/comic. It has no parameters.
435              
436             =item B<< get_filename >>
437              
438             get the full path to the file where L</getstore> will save the downloaded
439             data.
440              
441             Returns the filename. Accepts the following named parameters:
442              
443             =over
444              
445             =item B<< filename >>
446              
447             the filename to use. If not provided, L</guess_filename> will be called
448             to make a reasonable guess.
449              
450             If this parameter is a reference to a C<sub>, it will be invoked with
451             a reference to the object and all the available parameters passed
452             to the L</get_filename> method itself.
453              
454             =item B<< directory >>
455              
456             the directory where the file should be saved into. Defaults to what
457             L</get_directory> says.
458              
459             =back
460              
461             =item B<< guess_filename >>
462              
463             try to figure out a sensible filename for a downloaded strip.
464              
465             Returns an absolute path to a file. Requires a mandatory named
466             parameter C<response>, which should hold
467             a reference to a L<HTTP::Response> object.
468              
469             =item B<< normalise_filename >>
470              
471             try to normalise a file name, e.g. giving it a sensible extension and
472             ensuring that it will not become a hidden file in some systems.
473              
474             Accepts the following named parameters:
475              
476             =over
477              
478             =item B<< filename >> (mandatory)
479              
480             the filename to normalise;
481              
482             =item B<< response >> (mandatory)
483              
484             a L<HTTP::Response> object associated with the image whose filename you
485             would like to normalise.
486              
487             =back
488              
489             =item B<< guess_file_extension >>
490              
491             guess filename extension for image file based on the content type or
492             the image's first data octets.
493              
494             Returns the guessed file extension, or dies trying. Requires a mandatory
495             named parameter C<response>, which holds a reference to a L<HTTP::Response>
496             object.
497              
498             =item B<< id_to_uri >>
499              
500             turn a comic id into a URI pointing towards the image file.
501              
502             Accepts the id as the only parameter (non-named). Returns a URI.
503              
504             Note that this method has to be overridden in a derived plugin.
505              
506             =item B<< probe >>
507              
508             probe the remote site for available comics.
509              
510             Note that this method has to be overridden in a derived plugin.
511              
512             =item B<< get_config >>
513              
514             =item B<< set_config >>
515              
516             these are actually class methods and not instance methods, and are useful
517             to set plugin-specific configurations for each comic.
518              
519             It has to be set to a hash where keys are feature names, and the values can
520             be anything a plugin deems necessary. The hash is used by
521             L</get_comics_list> to return the names of the available features.
522              
523             C<get_config> returns the hash; depending on the call context (scalar or list),
524             it does the right thing. Accepts the optional named parameter C<probe>, to
525             trigger a L</probe> towards the remote site.
526              
527             C<set_config> accepts either a single reference to a hash, or a list that will
528             be turned into a hash. Returns nothing.
529              
530             =item B<< BUILD >>
531              
532             Not to be used directly.
533              
534             =back
535              
536             =head2 Download Related
537              
538             =over
539              
540             =item B<< get >>
541              
542             get the comic or die trying.
543              
544             Returns a L<HTTP::Response> object if successful, C<croak>s otherwise.
545              
546             Accepts a named parameter C<id> that is the identifier for the strip to
547             download; defaults to the id given back by L</get_current_id>.
548              
549             =item B<< getstore >>
550              
551             get the comic and saves to file, or die trying.
552              
553             Accepts the following named parameters:
554              
555             =over
556              
557             =item B<< id >>
558              
559             the identifier for the strip to
560             download; defaults to the id given back by L</get_current_id>.
561              
562             =item B<< filename >>
563              
564             =item B<< directory >>
565              
566             passed on to L</get_filename> to determine the final filename.
567              
568             =back
569              
570              
571             Accepts a name
572              
573             =item B<< get_http_response >>
574              
575             get the comic and return what the User Agent gives it back, whatever it is
576             (i.e. either a L<HTTP::Response> object, or C<undef>).
577              
578             Accepts a named parameter C<id> that is the identifier for the strip to
579             download; defaults to the id given back by L</get_current_id>.
580              
581             =back
582              
583             =head2 Adding A Plugin
584              
585             Integrating a new plugin requires that you derive your new module from
586             L<WWW::Comix::Plugin>, and that you override I<at least> the following
587             methods:
588              
589             =over
590              
591             =item *
592              
593             L</probe>
594              
595             =item *
596              
597             L</get_available_ids>
598              
599             =item *
600              
601             L</id_to_uri>
602              
603             =back
604              
605             Probing is where you set the available comics, together with any
606             information you think necessary. The L</probe> method must be regarded
607             as a class method, and is supposed to set the configuration hash
608             via L</set_config>. For example, if you already know that your plugin
609             is going to provide only the two strips C<Foo bars> and B<Baz the Great>,
610             you could do the following:
611              
612             sub probe {
613             my $sp = shift; # sp stands for "self or package"
614             $sp->set_config(
615             'Foo bars' => 'http://foo-bars.example.com/archive/',
616             'Baz the Great' => 'http://baz-the-great.example.com/btg/',
617             );
618             return;
619             }
620              
621             How you're going to use the values is up to you, you can set whatever
622             you want.
623              
624             As a general note, if you
625             foresee that the L</get_available_ids> can be too resource demanding
626             (e.g. because the whole list is spread over many pages), you should
627             turn to an iterator-based implementation like this:
628              
629             sub get_available_ids {
630             my $self = shift;
631             my @retval;
632             my $it = $self->get_id_iterator();
633             while (my $id = $it->()) {
634             push @retval, $id;
635             }
636             return @retval;
637             }
638              
639             sub get_id_iterator {
640             # Override the parent's method here, with a more complicated
641             # but less resource-demanding logic.
642             }
643              
644             You can see examples of this in L<WWW::Comix::Plugin::Creators> and
645             L<WWW::Comix::Plugin::GoComics>.
646              
647             =head1 DIAGNOSTICS
648              
649             Some errors can be generated by the Moose system; notably, if you call
650             the C<new> method without providing the C<comic> parameters, that is
651             mandatory.
652              
653             =over
654              
655             =item C<< derived classes MUST implement %s >>
656              
657             you're trying to use some plugin that didn't implement all the needed
658             methods, see L</Adding A Plugin>.
659              
660             =item C<< plugin %s: didn't probe, no configuration >>
661              
662             Before asking for available comics, or even get the configurations
663             specific to some plugin, you have to call L</probe>. You can pass the
664             probe parameter to the call, anyway.
665              
666             =item C<< couldn't get probe page '%s': %s >>
667              
668             something wrong with the Internet connection, apparently.
669              
670             =item C<< unhandled comic '%s' >>
671              
672             the given comic is not supported by this plugin.
673              
674             =item C<< open('%s'): %s >>
675              
676             this error can be given back by L</getstore> when trying to open a file
677             for writing the retrieved image data. The error given back by the
678             Operating System is reported in the error message.
679              
680             =item C<< can't guess a filename without a HTTP::Response >>
681              
682             L</guess_filename> needs at least a C<response> parameter holding a
683             reference to an L<HTTP::Response> object to work properly.
684              
685             =item C<< can't normalise a filename without a filename >>
686              
687             don't try to call L</normalise_filename> without providing a C<filename>
688             parameter. Ok, it's actually refusing to call your file C<0> actually.
689              
690             =item C<< can't guess file extension without a HTTP::Response >>
691              
692             nothing more to say, be sure to include a C<response> parameter holding
693             a reference to a L<HTTP::Response> object when calling
694             L</guess_file_extension> and L</normalise_filename>.
695              
696             =item C<< can't guess type of file, bailing out >>
697              
698             L</guess_file_extension> (which is called by L</normalise_filename>)
699             couldn't determine a suitable file extension for the given image. And
700             it tries hard.
701              
702             =item C<< error getting '%s': $s >>
703              
704             either L</get> or L</getstore> had problems downloading the given image.
705              
706             =back
707              
708              
709             =head1 AUTHOR
710              
711             Flavio Poletti C<< <flavio [at] polettix [dot] it> >>
712              
713              
714             =head1 LICENCE AND COPYRIGHT
715              
716             Copyright (c) 2008, Flavio Poletti C<< <flavio [at] polettix [dot] it> >>. All rights reserved.
717              
718             This module is free software; you can redistribute it and/or
719             modify it under the same terms as Perl 5.8.x itself. See L<perlartistic>
720             and L<perlgpl>.
721              
722             Questo modulo è software libero: potete ridistribuirlo e/o
723             modificarlo negli stessi termini di Perl 5.8.x stesso. Vedete anche
724             L<perlartistic> e L<perlgpl>.
725              
726              
727             =head1 DISCLAIMER OF WARRANTY
728              
729             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
730             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
731             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
732             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
733             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
734             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
735             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
736             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
737             NECESSARY SERVICING, REPAIR, OR CORRECTION.
738              
739             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
740             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
741             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
742             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
743             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
744             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
745             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
746             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
747             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
748             SUCH DAMAGES.
749              
750             =head1 NEGAZIONE DELLA GARANZIA
751              
752             Poiché questo software viene dato con una licenza gratuita, non
753             c'è alcuna garanzia associata ad esso, ai fini e per quanto permesso
754             dalle leggi applicabili. A meno di quanto possa essere specificato
755             altrove, il proprietario e detentore del copyright fornisce questo
756             software "così com'è" senza garanzia di alcun tipo, sia essa espressa
757             o implicita, includendo fra l'altro (senza però limitarsi a questo)
758             eventuali garanzie implicite di commerciabilità e adeguatezza per
759             uno scopo particolare. L'intero rischio riguardo alla qualità ed
760             alle prestazioni di questo software rimane a voi. Se il software
761             dovesse dimostrarsi difettoso, vi assumete tutte le responsabilità
762             ed i costi per tutti i necessari servizi, riparazioni o correzioni.
763              
764             In nessun caso, a meno che ciò non sia richiesto dalle leggi vigenti
765             o sia regolato da un accordo scritto, alcuno dei detentori del diritto
766             di copyright, o qualunque altra parte che possa modificare, o redistribuire
767             questo software così come consentito dalla licenza di cui sopra, potrà
768             essere considerato responsabile nei vostri confronti per danni, ivi
769             inclusi danni generali, speciali, incidentali o conseguenziali, derivanti
770             dall'utilizzo o dall'incapacità di utilizzo di questo software. Ciò
771             include, a puro titolo di esempio e senza limitarsi ad essi, la perdita
772             di dati, l'alterazione involontaria o indesiderata di dati, le perdite
773             sostenute da voi o da terze parti o un fallimento del software ad
774             operare con un qualsivoglia altro software. Tale negazione di garanzia
775             rimane in essere anche se i dententori del copyright, o qualsiasi altra
776             parte, è stata avvisata della possibilità di tali danneggiamenti.
777              
778             Se decidete di utilizzare questo software, lo fate a vostro rischio
779             e pericolo. Se pensate che i termini di questa negazione di garanzia
780             non si confacciano alle vostre esigenze, o al vostro modo di
781             considerare un software, o ancora al modo in cui avete sempre trattato
782             software di terze parti, non usatelo. Se lo usate, accettate espressamente
783             questa negazione di garanzia e la piena responsabilità per qualsiasi
784             tipo di danno, di qualsiasi natura, possa derivarne.
785              
786             =cut