File Coverage

lib/HTTP/Promise/Headers/ContentDisposition.pm
Criterion Covered Total %
statement 75 80 93.7
branch 27 34 79.4
condition 13 23 56.5
subroutine 14 16 87.5
pod 9 9 100.0
total 138 162 85.1


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Asynchronous HTTP Request and Promise - ~/lib/HTTP/Promise/Headers/ContentDisposition.pm
3             ## Version v0.1.0
4             ## Copyright(c) 2022 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2022/05/07
7             ## Modified 2022/05/07
8             ## All rights reserved.
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package HTTP::Promise::Headers::ContentDisposition;
15             BEGIN
16             {
17 7     7   3506 use strict;
  7         29  
  7         267  
18 7     7   148 use warnings;
  7         19  
  7         273  
19 7     7   51 use warnings::register;
  7         17  
  7         1596  
20 7     7   53 use parent qw( HTTP::Promise::Headers::Generic );
  7         12  
  7         58  
21 7     7   719 our $VERSION = 'v0.1.0';
22             };
23              
24 7     7   55 use strict;
  7         16  
  7         281  
25 7     7   48 use warnings;
  7         17  
  7         5676  
26              
27             sub init
28             {
29 45     45 1 85387 my $self = shift( @_ );
30 45         630 $self->{filename_charset} = undef;
31 45         140 $self->{filename_lang} = undef;
32 45 100 100     344 @_ = () if( @_ == 1 && $self->_is_a( $_[0] => 'Module::Generic::Null' ) );
33 45 100       1760 if( @_ )
34             {
35 39         104 my $str = shift( @_ );
36 39 50 33     220 return( $self->error( "No value was provided for Content-Disposition field." ) ) if( !defined( $str ) || !length( "$str" ) );
37 39         329 my $params = $self->_get_args_as_hash( @_ );
38 39   50     442 my $hv = $self->_parse_header_value( $str ) ||
39             return( $self->pass_error );
40 39 50       369 $hv->params( $params ) if( scalar( keys( %$params ) ) );
41 39         242 $self->_hv( $hv );
42             }
43 45 50       1869 $self->SUPER::init( @_ ) || return( $self->pass_error );
44 45         261 $self->_field_name( 'Content-Disposition' );
45 45         36660 return( $self );
46             }
47              
48 8     8 1 264805 sub as_string { return( shift->_hv_as_string( [qw( name filename )] ) ); }
49              
50             sub disposition
51             {
52 12     12 1 491127 my $self = shift( @_ );
53 12 100       52 if( @_ )
54             {
55 5   50     24 my $dispo = shift( @_ ) || return( $self->error( "No content disposition was provided." ) );
56 5         26 my $hv = $self->_hv;
57 5 100       139 if( $hv )
58             {
59 4         33 $hv->value( $dispo );
60             }
61             else
62             {
63 1   50     9 $hv = $self->_new_hv( $dispo ) || return( $self->pass_error );
64 1         177 $self->_hv( $hv );
65             }
66 5         3680 return( $dispo );
67             }
68             else
69             {
70             # No header value object, means there is just nothing set yet
71 7   50     31 my $hv = $self->_hv || return( '' );
72 7         327 return( $hv->value_data );
73             }
74             }
75              
76             sub filename
77             {
78 13     13 1 77480 my $self = shift( @_ );
79 13 100       44 if( @_ )
80             {
81 1         4 my( $fname, $lang ) = @_;
82 1 50       5 if( !defined( $fname ) )
83             {
84 0         0 $self->params->delete( 'filename' );
85 0         0 $self->params->delete( 'filename*' );
86             }
87             else
88             {
89 1   33     4 $lang //= $self->filename_lang;
90 1 50       10 if( my $enc = $self->_filename_encode( $fname, $lang ) )
91             {
92 1 50       297 $self->_set_get_param( 'filename*' => $enc ) || return( $self->pass_error );
93             }
94             else
95             {
96 0         0 $self->_set_get_qparam( filename => $fname );
97             }
98             }
99             }
100             else
101             {
102 12         81 my $v = $self->_set_get_qparam( 'filename' );
103 12 100 66     92 if( defined( $v ) && length( $v ) )
    100          
104             {
105             # decode if necessary
106 9         26 my( $f_charset, $f_lang );
107 9         71 ( $v, $f_charset, $f_lang ) = $self->_filename_decode( $v );
108 9         44 $self->filename_charset( $f_charset );
109 9         26624 $self->filename_lang( $f_lang );
110             }
111             elsif( $v = $self->_set_get_param( 'filename*' ) )
112             {
113 1         519 my( $f_charset, $f_lang );
114 1         6 ( $v, $f_charset, $f_lang ) = $self->_filename_decode( $v );
115 1         6 $self->filename_charset( $f_charset );
116 1         929 $self->filename_lang( $f_lang );
117             }
118 12         11235 return( $v );
119             }
120             }
121              
122             sub filename_charset
123             {
124 11     11 1 31 my $self = shift( @_ );
125 11 100       37 if( @_ )
126             {
127 10         22 my $v = shift( @_ );
128 10 100       29 if( !defined( $v ) )
129             {
130 5         38 return( $self->_set_get_scalar_as_object( 'filename_charset', $v ) );
131             }
132 5 100 66     36 return( $self->error( "Only supported charset is 'utf-8'." ) ) if( lc( $v ) ne 'utf-8' && lc( $v ) ne 'utf8' );
133             # Convenience
134 1 50       6 $v = 'utf-8' if( lc( $v ) eq 'utf8' );
135 1         4 $v = uc( $v );
136 1         6 return( $self->_set_get_scalar_as_object( 'filename_charset', $v ) );
137             }
138 1         5 return( $self->_set_get_scalar_as_object( 'filename_charset' ) );
139             }
140              
141 11     11 1 1128 sub filename_lang { return( shift->_set_get_scalar_as_object( 'filename_lang', @_ ) ); }
142              
143 18     18 1 76607 sub name { return( shift->_set_get_param( name => @_ ) ); }
144              
145 0     0 1   sub param { return( shift->_set_get_param( @_ ) ); }
146              
147 0     0 1   sub params { return( shift->_set_get_params( @_ ) ); }
148              
149             1;
150             # NOTE: POD
151             __END__
152              
153             =encoding utf-8
154              
155             =head1 NAME
156              
157             HTTP::Promise::Headers::ContentDisposition - Content-Disposition Header Field
158              
159             =head1 SYNOPSIS
160              
161             use HTTP::Promise::Headers::ContentDisposition;
162             my $cd = HTTP::Promise::Headers::ContentDisposition->new ||
163             die( HTTP::Promise::Headers::ContentDisposition->error, "\n" );
164             my $dispo = $cd->disposition;
165             # For example, attachment
166             $cd->disposition( 'inline' );
167             $cd->filename( 'some-file.txt' );
168             $cd->disposition( 'form-data' );
169             $cd->name( 'someField' );
170             my $name = $cd->name;
171             # Same thing
172             my $name = $cd->param( 'name' );
173             $cd->params( name => 'someField', filename => 'some-file.txt' );
174              
175             =head1 VERSION
176              
177             v0.1.0
178              
179             =head1 DESCRIPTION
180              
181             The following description is taken from Mozilla documentation.
182              
183             Content-Disposition: inline
184             Content-Disposition: attachment
185             Content-Disposition: attachment; filename="filename.jpg"
186             Content-Disposition: form-data; name="fieldName"
187             Content-Disposition: form-data; name="fieldName"; filename="filename.jpg"
188              
189             =head1 METHODS
190              
191             =head2 as_string
192              
193             Returns a string representation of the Content-Disposition object.
194              
195             =head2 disposition
196              
197             Sets or gets the type of C<Content-Disposition> this is. For example: C<attachment>, C<form-data>
198              
199             =head2 name
200              
201             Is followed by a string containing the name of the HTML field in the form that the content of this subpart refers to. When dealing with multiple files in the same field (for example, the multiple attribute of an <input type="file"> element), there can be several subparts with the same name.
202              
203             A name with a value of '_charset_' indicates that the part is not an HTML field, but the default charset to use for parts without explicit charset information.
204              
205             =head2 filename
206              
207             Without any argument, this returns the string containing the original name of the file transmitted. The C<filename> is always optional and must not be used blindly by the application: path information should be stripped, and conversion to the server file system rules should be done. This parameter provides mostly indicative information. When used in combination with C<Content-Disposition: attachment>, it is used as the default C<filename> for an eventual "Save As" dialog presented to the user.
208              
209             If the property C<filename*> is set instead, then it will be decoded and used instead, and the value for L</filename_charset> and L</filename_lang> will be set.
210              
211             When setting the filename value, this takes an optional language iso 639 code (see L<rfc5987|https://tools.ietf.org/html/rfc5987> and L<rfc2231|https://tools.ietf.org/html/rfc2231>).
212             If the filename contains non ascii characters, it will be automatically encoded according to L<rfc5987|https://tools.ietf.org/html/rfc5987>. and the property C<filename*> set instead. That property, by rfc standard, takes precedence over the C<filename> one.
213              
214             The language provided, if any, will be used then.
215              
216             For example:
217              
218             $h->disposition( 'form-data' );
219             $h->name( 'fileField' );
220             $h->filename( q{file.txt} );
221             say "$h";
222             # form-data; name="fileField"; filename="file.txt"
223              
224             $h->disposition( 'form-data' );
225             $h->name( 'fileField' );
226             $h->filename( q{ファイル.txt} );
227             say "$h";
228             # form-data; name="fileField"; filename*="UTF-8''%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.txt"
229            
230             $h->disposition( 'form-data' );
231             $h->name( 'fileField' );
232             $h->filename( q{ファイル.txt}, 'ja-JP' );
233             say "$h";
234             # form-data; name="fileField"; filename*="UTF-8'ja-JP'%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.txt"
235              
236             # Using default value
237             $h->filename_lang( 'ja-JP' );
238             $h->disposition( 'form-data' );
239             $h->name( 'fileField' );
240             $h->filename( q{ファイル.txt} );
241             say "$h";
242             # form-data; name="fileField"; filename*="UTF-8'ja-JP'%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.txt"
243              
244             $headers->header( Content_Disposition => "$h" );
245              
246             The C<Content-Disposition> header value would then contain a property C<filename*> (with the trailing wildcard).
247              
248             See also L<HTTP::Promise::Headers/decode_filename> and L<HTTP::Promise::Headers/encode_filename> which are used to decode and encode filenames.
249              
250             =head2 filename_charset
251              
252             Sets or gets the encoded filename charset.
253              
254             This is used when the filename contains non-ascii characters, such as Japanese, Korean, or Cyrillic.
255             Although theoretically one can set any character set, by design this only accepts C<UTF-8> (case insensitive).
256              
257             This is set automatically when calling L</filename>. You actually need to call L</filename> first to have a value set.
258              
259             Returns a L<scalar object|Module::Generic::Scalar> containing the filename charset.
260              
261             =head2 filename_lang
262              
263             Sets or gets the encoded filename language. This takes an iso 639 language code (see L<rfc1766|https://tools.ietf.org/html/rfc1766>).
264              
265             This is set automatically when calling L</filename>. You actually need to call L</filename> first to have a value set.
266              
267             Returns a L<scalar object|Module::Generic::Scalar> containing the filename language.
268              
269             =head2 param
270              
271             Sets or gets an arbitrary C<Content-Disposition> property.
272              
273             Note that if you use this, you bypass other specialised method who do some additional processing, so be mindful.
274              
275             =head2 params
276              
277             Sets or gets multiple arbitrary C<Content-Disposition> properties at once.
278              
279             If called without any arguments, this returns the L<hash object|Module::Generic::Hash> used to store the C<Content-Disposition> properties.
280              
281             =head1 AUTHOR
282              
283             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
284              
285             =head1 SEE ALSO
286              
287             See L<rfc6266, section 4|https://tools.ietf.org/html/rfc6266#section-4>, L<rfc7578, section 4.2|https://tools.ietf.org/html/rfc7578#section-4.2> and L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition>
288              
289             L<HTTP::Promise>, L<HTTP::Promise::Request>, L<HTTP::Promise::Response>, L<HTTP::Promise::Message>, L<HTTP::Promise::Entity>, L<HTTP::Promise::Headers>, L<HTTP::Promise::Body>, L<HTTP::Promise::Body::Form>, L<HTTP::Promise::Body::Form::Data>, L<HTTP::Promise::Body::Form::Field>, L<HTTP::Promise::Status>, L<HTTP::Promise::MIME>, L<HTTP::Promise::Parser>, L<HTTP::Promise::IO>, L<HTTP::Promise::Stream>, L<HTTP::Promise::Exception>
290              
291             =head1 COPYRIGHT & LICENSE
292              
293             Copyright(c) 2022 DEGUEST Pte. Ltd.
294              
295             All rights reserved.
296              
297             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
298              
299             =cut