line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=head1 NAME |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
XAO::DO::Web::Config - XAO::Web site configuration object |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 SYNOPSIS |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
sub init { |
8
|
|
|
|
|
|
|
my $self=shift; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
my $webconfig=XAO::Objects->new(objname => 'Web::Config'); |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
$self->embed(web => $webconfig); |
13
|
|
|
|
|
|
|
} |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 DESCRIPTION |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
This object provides methods specifically for XAO::Web objects. It is |
18
|
|
|
|
|
|
|
supposed to be embedded into XAO::DO::Config object by a web server |
19
|
|
|
|
|
|
|
handler when site is initialized. |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=cut |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
############################################################################### |
24
|
|
|
|
|
|
|
package XAO::DO::Web::Config; |
25
|
20
|
|
|
20
|
|
14261
|
use warnings; |
|
20
|
|
|
|
|
83
|
|
|
20
|
|
|
|
|
1072
|
|
26
|
20
|
|
|
20
|
|
144
|
use strict; |
|
20
|
|
|
|
|
58
|
|
|
20
|
|
|
|
|
489
|
|
27
|
20
|
|
|
20
|
|
9886
|
use CGI::Cookie; |
|
20
|
|
|
|
|
103136
|
|
|
20
|
|
|
|
|
889
|
|
28
|
20
|
|
|
20
|
|
10168
|
use POSIX qw(mktime); |
|
20
|
|
|
|
|
131916
|
|
|
20
|
|
|
|
|
571
|
|
29
|
20
|
|
|
20
|
|
28581
|
use XAO::Cache; |
|
20
|
|
|
|
|
46
|
|
|
20
|
|
|
|
|
573
|
|
30
|
20
|
|
|
20
|
|
108
|
use XAO::Errors qw(XAO::DO::Web::Config); |
|
20
|
|
|
|
|
40
|
|
|
20
|
|
|
|
|
130
|
|
31
|
20
|
|
|
20
|
|
7849
|
use XAO::Objects; |
|
20
|
|
|
|
|
50
|
|
|
20
|
|
|
|
|
460
|
|
32
|
20
|
|
|
20
|
|
111
|
use XAO::Utils; |
|
20
|
|
|
|
|
50
|
|
|
20
|
|
|
|
|
1439
|
|
33
|
|
|
|
|
|
|
|
34
|
20
|
|
|
20
|
|
147
|
use base XAO::Objects->load(objname => 'Embeddable'); |
|
20
|
|
|
|
|
52
|
|
|
20
|
|
|
|
|
87
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
our $VERSION='2.004'; # Obsolete, but needed by CPAN |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# Prototypes |
39
|
|
|
|
|
|
|
# |
40
|
|
|
|
|
|
|
sub add_cookie ($@); |
41
|
|
|
|
|
|
|
sub cgi ($$); |
42
|
|
|
|
|
|
|
sub cleanup ($); |
43
|
|
|
|
|
|
|
sub clipboard ($); |
44
|
|
|
|
|
|
|
sub cookies ($); |
45
|
|
|
|
|
|
|
sub disable_special_access ($); |
46
|
|
|
|
|
|
|
sub embeddable_methods ($); |
47
|
|
|
|
|
|
|
sub enable_special_access ($); |
48
|
|
|
|
|
|
|
sub force_byte_output ($;$); |
49
|
|
|
|
|
|
|
sub get_cookie ($$;$); |
50
|
|
|
|
|
|
|
sub header ($@); |
51
|
|
|
|
|
|
|
sub header_args ($@); |
52
|
|
|
|
|
|
|
sub header_array ($); |
53
|
|
|
|
|
|
|
sub header_normalize ($$); |
54
|
|
|
|
|
|
|
sub header_printed ($); |
55
|
|
|
|
|
|
|
sub header_remove ($@); |
56
|
|
|
|
|
|
|
sub new ($@); |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
############################################################################### |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head1 METHODS |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=over |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=cut |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
############################################################################### |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub _cookie_config ($$) { |
69
|
48
|
|
|
48
|
|
92
|
my ($self,$cookie_name)=@_; |
70
|
|
|
|
|
|
|
|
71
|
48
|
|
|
|
|
63
|
my $defaults; |
72
|
|
|
|
|
|
|
|
73
|
48
|
|
|
|
|
124
|
my $base_config=$self->base_config; |
74
|
48
|
50
|
33
|
|
|
417
|
if($base_config && $base_config->can('get')) { |
75
|
48
|
100
|
|
|
|
1002
|
if(my $cookie_config=$base_config->get('/xao/cookie')) { |
76
|
25
|
100
|
|
|
|
1702
|
foreach my $cf ($cookie_config->{'common'}, (exists $cookie_config->{$cookie_name} ? ($cookie_config->{$cookie_name}) : ())) { |
77
|
32
|
50
|
|
|
|
66
|
$cf || next; |
78
|
|
|
|
|
|
|
|
79
|
32
|
|
|
|
|
78
|
foreach my $n (keys %$cf) { |
80
|
106
|
100
|
|
|
|
237
|
my $nn=$n =~ /^-/ ? $n : '-'.lc($n); |
81
|
106
|
|
|
|
|
231
|
$defaults->{$nn}=$cf->{$n}; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
48
|
|
|
|
|
955
|
return $defaults; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
############################################################################### |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=item add_cookie (@) |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
Adds an HTTP cookie into the internal list. Parameters are a hash in the |
95
|
|
|
|
|
|
|
same format as for CGI->cookie() method (see L). |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
If a cookie with the same name, path (and domain if set) is already in |
98
|
|
|
|
|
|
|
the list from a previous call to add_cookie() then it gets replaced. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Think of it as if you are adding cookies to you final HTTP response as |
101
|
|
|
|
|
|
|
XAO::Web handler will get all the cookies collected during template |
102
|
|
|
|
|
|
|
processing and send them out for you. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
Examples: |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
$config->add_cookie($cookie); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
$config->add_cookie( |
109
|
|
|
|
|
|
|
-name => 'sessionID', |
110
|
|
|
|
|
|
|
-value => 'xyzzy', |
111
|
|
|
|
|
|
|
-expires =>'+1h', |
112
|
|
|
|
|
|
|
); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
For convenience, if there is a '-domain' argument and it refers to a |
115
|
|
|
|
|
|
|
list of domains the cookie is expanded into a set of cookies for all |
116
|
|
|
|
|
|
|
these domains. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Parameters may also be configured as defaults or for specific cookie |
119
|
|
|
|
|
|
|
names in the site configuration /xao/cookie section: |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
'xao' => { |
122
|
|
|
|
|
|
|
'cookie' => { |
123
|
|
|
|
|
|
|
'common' => { # All cookies |
124
|
|
|
|
|
|
|
'httponly' => 1, |
125
|
|
|
|
|
|
|
'secure' => 1, |
126
|
|
|
|
|
|
|
}, |
127
|
|
|
|
|
|
|
'sessionid' => { # Specific cookie |
128
|
|
|
|
|
|
|
'samesite' => 'None', |
129
|
|
|
|
|
|
|
}, |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
Due to incompatible ways various browsers treat "SameSite" cookies a |
132
|
|
|
|
|
|
|
special parameter 'sscompat' can be used to create and read two cookies -- |
133
|
|
|
|
|
|
|
one with "SameSite" value and one without. This alters the behavior of |
134
|
|
|
|
|
|
|
both add_cookie() and get_cookie() methods for simplicity. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
'sessionid' => { |
137
|
|
|
|
|
|
|
'samesite' => 'None', |
138
|
|
|
|
|
|
|
'sscompat' => 1, |
139
|
|
|
|
|
|
|
}, |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
$siteconfig->add_cookie(-name => 'sessionid', -value => $sessionid); |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
Set-Cookie: ...; sessionid=12345; Secure; HttpOnly; SameSite=None |
144
|
|
|
|
|
|
|
Set-Cookie: ...; sessionid-sscompat=12345; Secure; HttpOnly |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
The get_cookie() method takes into consideration values that were set |
147
|
|
|
|
|
|
|
with add_cookie() as a priority over CGI cookies received. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=cut |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub add_cookie ($@) { |
152
|
41
|
|
|
41
|
1
|
4070
|
my $self=shift; |
153
|
41
|
100
|
|
|
|
137
|
my $cookie=(@_==1 ? $_[0] : get_args(\@_)); |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# We should only be getting hash based cookie data. Attempting to |
156
|
|
|
|
|
|
|
# unbake it if we got a baked one. |
157
|
|
|
|
|
|
|
# |
158
|
41
|
100
|
|
|
|
454
|
if(!ref($cookie)) { |
159
|
4
|
|
|
|
|
26
|
eprint "Passing baked cookies to ".$self."::add_cookie() is STRONGLY DEPRECATED!"; |
160
|
|
|
|
|
|
|
|
161
|
20
|
|
|
20
|
|
26678
|
use CGI::Util qw(); |
|
20
|
|
|
|
|
43
|
|
|
20
|
|
|
|
|
44095
|
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# Expecting something like: |
164
|
|
|
|
|
|
|
# foo=bar; path=/; expires=Wed, 16-Dec-2015 03:32:33 GMT |
165
|
|
|
|
|
|
|
# |
166
|
4
|
|
|
|
|
208
|
my ($nv,@params)=split(/\s*;\s*/,$cookie); |
167
|
|
|
|
|
|
|
|
168
|
4
|
|
|
|
|
10
|
my ($name,$value); |
169
|
4
|
50
|
|
|
|
30
|
if($nv=~/^\s*(.+)\s*=\s*(.*?)\s*$/) { |
170
|
4
|
|
|
|
|
14
|
$name=CGI::Util::unescape($1); |
171
|
4
|
|
|
|
|
71
|
$value=[map { CGI::Util::unescape($_) } split(/&/,$2)]; |
|
4
|
|
|
|
|
9
|
|
172
|
4
|
50
|
|
|
|
65
|
$value=$value->[0] if @$value==1; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
else { |
175
|
0
|
|
|
|
|
0
|
eprint "Unparsable baked cookie '$cookie' in add_cookie(), NOT SET"; |
176
|
0
|
|
|
|
|
0
|
return; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
4
|
|
|
|
|
15
|
my %chash=( |
180
|
|
|
|
|
|
|
-name => $name, |
181
|
|
|
|
|
|
|
-value => $value, |
182
|
|
|
|
|
|
|
); |
183
|
|
|
|
|
|
|
|
184
|
4
|
|
|
|
|
10
|
foreach my $p (@params) { |
185
|
8
|
50
|
|
|
|
49
|
$p=~/^\s*(.+)\s*=\s*(.*?)\s*$/ || next; |
186
|
8
|
|
|
|
|
26
|
my ($pn,$pv)=(lc($1),$2); |
187
|
8
|
50
|
|
|
|
26
|
if ($pn eq 'domain') { $chash{'-domain'}=$pv; } |
|
0
|
100
|
|
|
|
0
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
188
|
4
|
|
|
|
|
10
|
elsif($pn eq 'path') { $chash{'-path'}=$pv; } |
189
|
4
|
|
|
|
|
11
|
elsif($pn eq 'expires') { $chash{'-expires'}=$pv; } |
190
|
0
|
|
|
|
|
0
|
elsif($pn eq 'max-age') { $chash{'-max-age'}=$pv; } |
191
|
0
|
|
|
|
|
0
|
elsif($pn eq 'secure') { $chash{'-secure'}=$pv; } |
192
|
0
|
|
|
|
|
0
|
elsif($pn eq 'httponly'){ $chash{'-httponly'}=$pv; } |
193
|
0
|
|
|
|
|
0
|
elsif($pn eq 'samesite'){ $chash{'-samesite'}=$pv; } |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
4
|
|
|
|
|
12
|
$cookie=\%chash; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
41
|
50
|
|
|
|
89
|
if(!$cookie->{'-name'}) { |
200
|
0
|
|
|
|
|
0
|
eprint "No cookie name given to ".ref($self)."::add_cookie()"; |
201
|
0
|
|
|
|
|
0
|
return; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# Applying configuration values, if any. |
205
|
|
|
|
|
|
|
# |
206
|
41
|
|
|
|
|
88
|
my $defaults=$self->_cookie_config($cookie->{'-name'}); |
207
|
41
|
100
|
|
|
|
111
|
$cookie=merge_refs($defaults, $cookie) if $defaults; |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# SameSite parameter is not implemented the same in all |
210
|
|
|
|
|
|
|
# browsers. When requested we set an additional cookie without that |
211
|
|
|
|
|
|
|
# parameter to work in incompatible browsers. |
212
|
|
|
|
|
|
|
# |
213
|
41
|
50
|
66
|
|
|
437
|
if($cookie->{'-sscompat'} && $cookie->{'-samesite'}) { |
214
|
|
|
|
|
|
|
$self->add_cookie(merge_refs($cookie, { |
215
|
5
|
|
|
|
|
23
|
-name => $cookie->{'-name'}.'-sscompat', |
216
|
|
|
|
|
|
|
-sscompat => undef, |
217
|
|
|
|
|
|
|
-samesite => undef, |
218
|
|
|
|
|
|
|
})); |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# Recursively expanding if multiple domains are given. |
222
|
|
|
|
|
|
|
# |
223
|
41
|
100
|
100
|
|
|
106
|
if($cookie->{'-domain'} && ref($cookie->{'-domain'})) { |
224
|
2
|
|
|
|
|
8
|
my $dlist=$cookie->{'-domain'}; |
225
|
2
|
|
|
|
|
6
|
foreach my $domain (@$dlist) { |
226
|
4
|
|
|
|
|
17
|
$self->add_cookie(merge_refs($cookie,{ |
227
|
|
|
|
|
|
|
-domain => $domain, |
228
|
|
|
|
|
|
|
})); |
229
|
|
|
|
|
|
|
} |
230
|
2
|
|
|
|
|
17
|
return; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
# If the new cookie has the same name, domain and path |
233
|
|
|
|
|
|
|
# as previously set one - we replace it. |
234
|
|
|
|
|
|
|
# |
235
|
39
|
100
|
|
|
|
81
|
if($self->{'cookies'}) { |
236
|
27
|
|
|
|
|
87
|
my $cnew=CGI::Cookie->new($cookie); |
237
|
|
|
|
|
|
|
|
238
|
27
|
|
|
|
|
7048
|
for(my $i=0; $i!=@{$self->{'cookies'}}; $i++) { |
|
114
|
|
|
|
|
1267
|
|
239
|
96
|
|
|
|
|
153
|
my $c=$self->{'cookies'}->[$i]; |
240
|
|
|
|
|
|
|
|
241
|
96
|
50
|
33
|
|
|
371
|
next unless ref($c) && ref($c) eq 'HASH'; |
242
|
|
|
|
|
|
|
|
243
|
96
|
|
|
|
|
219
|
my $cstored=CGI::Cookie->new($c); |
244
|
|
|
|
|
|
|
|
245
|
96
|
|
|
|
|
25326
|
my $dnew=$cnew->domain(); |
246
|
96
|
|
|
|
|
553
|
my $dstored=$cstored->domain(); |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
### dprint "...comparing ",$cnew->name()," with ",$cstored->name(),"; path='",$cnew->path(),"' vs '",$cstored->path(),"'; domain='",$dnew,"' vs '",$dstored,"'"; |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
next unless |
251
|
96
|
100
|
66
|
|
|
532
|
$cnew->name() eq $cstored->name() && |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
252
|
|
|
|
|
|
|
$cnew->path() eq $cstored->path() && |
253
|
|
|
|
|
|
|
((!defined($dnew) && !defined($dstored)) || (defined($dnew) && defined($dstored) && $dnew eq $dstored)); |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
### dprint "....override!"; |
256
|
|
|
|
|
|
|
|
257
|
9
|
|
|
|
|
212
|
$self->{'cookies'}->[$i]=$cookie; |
258
|
|
|
|
|
|
|
|
259
|
9
|
|
|
|
|
55
|
return $cookie; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
30
|
|
|
|
|
38
|
push(@{$self->{'cookies'}},$cookie); |
|
30
|
|
|
|
|
75
|
|
264
|
|
|
|
|
|
|
|
265
|
30
|
|
|
|
|
107
|
return $cookie; |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
############################################################################### |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=item cgi (;$) |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
Returns or sets standard CGI object (see L). In future versions this |
273
|
|
|
|
|
|
|
would probably be converted to CGI::Lite or something similar, so do not |
274
|
|
|
|
|
|
|
rely to much on the functionality of CGI. |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Obviously you should not call this method to set CGI object unless you |
277
|
|
|
|
|
|
|
are 100% sure you know what you're doing. And even in that case you have |
278
|
|
|
|
|
|
|
to call enable_special_access() in advance. |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
Example: |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
my $cgi=$self->cgi; |
283
|
|
|
|
|
|
|
my $name=$cgi->param('name'); |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
Or just: |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
my $name=$self->cgi->param('name'); |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=cut |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
sub cgi ($$) { |
292
|
352
|
|
|
352
|
1
|
1972
|
my ($self,$newcgi)=@_; |
293
|
|
|
|
|
|
|
|
294
|
352
|
100
|
|
|
|
1225
|
return $self->{'cgi'} unless $newcgi; |
295
|
|
|
|
|
|
|
|
296
|
140
|
50
|
|
|
|
413
|
if($self->{'special_access'}) { |
297
|
140
|
|
|
|
|
267
|
$self->{'cgi'}=$newcgi; |
298
|
140
|
|
|
|
|
312
|
return $newcgi; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
throw XAO::E::DO::Web::Config |
302
|
0
|
|
|
|
|
0
|
"cgi - storing new CGI requires enable_special_access()"; |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
############################################################################### |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=item cleanup () |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
Removes CGI object, cleans up clipboard. No need to call manually, |
310
|
|
|
|
|
|
|
usually is called as part of XAO::DO::Config cleanup(). |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=cut |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub cleanup ($) { |
315
|
270
|
|
|
270
|
1
|
13680
|
my $self=shift; |
316
|
270
|
|
|
|
|
602
|
delete $self->{'cgi'}; |
317
|
270
|
|
|
|
|
827
|
delete $self->{'clipboard'}; |
318
|
270
|
|
|
|
|
422
|
delete $self->{'cookies'}; |
319
|
270
|
|
|
|
|
438
|
delete $self->{'header_args'}; |
320
|
270
|
|
|
|
|
358
|
delete $self->{'force_byte_output'}; |
321
|
270
|
|
|
|
|
387
|
delete $self->{'header_printed'}; |
322
|
270
|
|
|
|
|
580
|
delete $self->{'special_access'}; |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
############################################################################### |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=item clipboard () |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
Returns clipboard XAO::SimpleHash object. Useful to keep temporary data |
330
|
|
|
|
|
|
|
between different XAO::Web objects. Cleaned up for every session. |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
=cut |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
sub clipboard ($) { |
335
|
1105
|
|
|
1105
|
1
|
6467
|
my $self=shift; |
336
|
1105
|
100
|
|
|
|
3032
|
$self->{'clipboard'}=XAO::SimpleHash->new() unless $self->{'clipboard'}; |
337
|
1105
|
|
|
|
|
3760
|
return $self->{'clipboard'}; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
############################################################################### |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=item cookies () |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
Returns reference to an array of prepared cookies. |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=cut |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
sub cookies ($) { |
349
|
23
|
|
|
23
|
1
|
412
|
my $self=shift; |
350
|
|
|
|
|
|
|
|
351
|
23
|
|
|
|
|
31
|
my @baked; |
352
|
23
|
|
|
|
|
35
|
foreach my $c (@{$self->{'cookies'}}) { |
|
23
|
|
|
|
|
58
|
|
353
|
30
|
50
|
33
|
|
|
4656
|
if(ref($c) && ref($c) eq 'HASH') { |
354
|
30
|
|
|
|
|
43
|
push @baked,CGI::Cookie->new(%{$c}); |
|
30
|
|
|
|
|
134
|
|
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
else { |
357
|
0
|
|
|
|
|
0
|
push @baked,$c; |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
23
|
|
|
|
|
2933
|
return \@baked; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
############################################################################### |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=item disable_special_access () |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
Disables use of cgi() method to set a new value. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=cut |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
sub disable_special_access ($) { |
373
|
132
|
|
|
132
|
1
|
1223
|
my $self=shift; |
374
|
132
|
|
|
|
|
334
|
delete $self->{special_access}; |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
############################################################################### |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=item embeddable_methods () |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
Used internally by global Config object, returns an array with all |
382
|
|
|
|
|
|
|
embeddable method names -- add_cookie(), cgi(), clipboard(), cookies(), |
383
|
|
|
|
|
|
|
force_byte_output(), header(), header_args(). |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=cut |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
sub embeddable_methods ($) { |
388
|
38
|
|
|
38
|
1
|
3294
|
qw( |
389
|
|
|
|
|
|
|
add_cookie cgi clipboard cookies force_byte_output |
390
|
|
|
|
|
|
|
header header_args header_array header_remove get_cookie |
391
|
|
|
|
|
|
|
); |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
############################################################################### |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=item enable_special_access () |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
Enables use of cgi() method to set a new value. Normally you do |
399
|
|
|
|
|
|
|
not need this method. |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
Example: |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
$config->enable_special_access(); |
404
|
|
|
|
|
|
|
$config->cgi(CGI->new()); |
405
|
|
|
|
|
|
|
$config->disable_special_access(); |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=cut |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
sub enable_special_access ($) { |
410
|
140
|
|
|
140
|
1
|
1374
|
my $self=shift; |
411
|
140
|
|
|
|
|
389
|
$self->{special_access}=1; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
############################################################################### |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
=item force_byte_output () |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
If the site is configured to run in character mode it might still be |
419
|
|
|
|
|
|
|
necessary to output some content as is, without character processing |
420
|
|
|
|
|
|
|
(e.g. for generated images or spreadsheets). |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
This method is called automatically when content type is set to a |
423
|
|
|
|
|
|
|
non-text value, so normally there is no need to call it directly. |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=cut |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
sub force_byte_output ($;$) { |
428
|
119
|
|
|
119
|
1
|
3919
|
my ($self,$value)=@_; |
429
|
119
|
100
|
|
|
|
276
|
if(defined $value) { |
430
|
33
|
|
|
|
|
55
|
$self->{'force_byte_output'}=$value; |
431
|
|
|
|
|
|
|
} |
432
|
119
|
|
|
|
|
339
|
return $self->{'force_byte_output'}; |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
############################################################################### |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=item header (@) |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
Returns HTTP header. The same as $cgi->header and accepts the same |
440
|
|
|
|
|
|
|
parameters. Cookies added before by add_cookie() method are also |
441
|
|
|
|
|
|
|
included in the header. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
Returns header only once, on subsequent calls returns undef. |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
B In mod_perl environment CGI will send the header itself and |
446
|
|
|
|
|
|
|
return empty string. Be carefull to check the result for |
447
|
|
|
|
|
|
|
C instead of just C! |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
As with the most of Web::Config methods you do not need this method |
450
|
|
|
|
|
|
|
normally. It is called automatically by web server handler at the end of |
451
|
|
|
|
|
|
|
a session before sending out session results. |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=cut |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
sub header ($@) { |
456
|
11
|
|
|
11
|
1
|
63
|
my $self=shift; |
457
|
|
|
|
|
|
|
|
458
|
11
|
50
|
|
|
|
24
|
return undef if $self->{'header_printed'}; |
459
|
|
|
|
|
|
|
|
460
|
11
|
50
|
|
|
|
24
|
$self->header_args(@_) if @_; |
461
|
|
|
|
|
|
|
|
462
|
11
|
|
|
|
|
21
|
$self->{'header_printed'}=1; |
463
|
|
|
|
|
|
|
|
464
|
11
|
|
|
|
|
21
|
return $self->cgi->header($self->header_array()); |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
############################################################################### |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
sub header_array ($) { |
470
|
11
|
|
|
11
|
0
|
19
|
my $self=shift; |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# There is a silly bug (or a truly misguided undocumented feature) |
473
|
|
|
|
|
|
|
# in CGI. It works with headers correctly only if the first header |
474
|
|
|
|
|
|
|
# it gets starts with a dash. We used to supply CGI::header() with a |
475
|
|
|
|
|
|
|
# hash and that resulted in sometimes un-dashed elements getting to |
476
|
|
|
|
|
|
|
# be the first in the list, resulting in mayhem -- completely broken |
477
|
|
|
|
|
|
|
# header output like this sent without any warnings: |
478
|
|
|
|
|
|
|
# |
479
|
|
|
|
|
|
|
# HTTP/1.0 foo |
480
|
|
|
|
|
|
|
# Server: Apache |
481
|
|
|
|
|
|
|
# Status: foo |
482
|
|
|
|
|
|
|
# Window-Target: ARRAY(0xc5f5e8) |
483
|
|
|
|
|
|
|
# P3P: policyref="/w3c/p3p.xml", CP="-expires" |
484
|
|
|
|
|
|
|
# Set-Cookie: -cookie |
485
|
|
|
|
|
|
|
# Expires: -Charset |
486
|
|
|
|
|
|
|
# Date: Thu, 07 Aug 2014 22:41:35 GMT |
487
|
|
|
|
|
|
|
# Content-Disposition: attachment; filename="no-cache" |
488
|
|
|
|
|
|
|
# Now |
489
|
|
|
|
|
|
|
# Content-Type: P3P; charset=-cache_control |
490
|
|
|
|
|
|
|
# |
491
|
|
|
|
|
|
|
# This never happened in years of using perl below version 5.18, |
492
|
|
|
|
|
|
|
# probably due to different internal hash algorithm that never |
493
|
|
|
|
|
|
|
# put undashed elements to the front. |
494
|
|
|
|
|
|
|
# |
495
|
|
|
|
|
|
|
# Using the always present '-cookie' header to fill the front row. |
496
|
|
|
|
|
|
|
# |
497
|
11
|
|
50
|
|
|
24
|
my $header_args=$self->{'header_args'} || { }; |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
return ( |
500
|
11
|
|
50
|
|
|
68
|
'-cookie' => ($header_args->{'-cookie'} || $header_args->{'Cookie'} || $self->cookies || []), |
501
|
|
|
|
|
|
|
%$header_args, |
502
|
|
|
|
|
|
|
); |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
############################################################################### |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
sub header_printed ($) { |
508
|
0
|
|
|
0
|
0
|
0
|
my $self=shift; |
509
|
0
|
|
|
|
|
0
|
return $self->{'header_printed'}; |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
############################################################################### |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=item header_args (%) |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
Sets some parameters for header generation. You can use it to change |
517
|
|
|
|
|
|
|
page status for example: |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
$config->header_args(-Status => '404 File not found'); |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
Accepts the same arguments CGI->header() accepts. |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
Header names can be any of 'Header-Name', 'header-name', 'header_name', |
524
|
|
|
|
|
|
|
or '-Header_name'. All variants are normalized to all-lowercase |
525
|
|
|
|
|
|
|
underscored to make values assigned later in the code trump the earlier. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
Supplying 'undef' as a header value is the same as removing that header |
528
|
|
|
|
|
|
|
with header_remove(). |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=cut |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
sub header_args ($@) { |
533
|
315
|
|
|
315
|
1
|
1996
|
my $self=shift; |
534
|
315
|
|
|
|
|
886
|
my $args=get_args(\@_); |
535
|
|
|
|
|
|
|
|
536
|
315
|
|
|
|
|
3759
|
@{$self->{'header_args'}}{map { $self->header_normalize($_) } keys %{$args}}=values %{$args}; |
|
315
|
|
|
|
|
1052
|
|
|
394
|
|
|
|
|
778
|
|
|
315
|
|
|
|
|
705
|
|
|
315
|
|
|
|
|
837
|
|
537
|
|
|
|
|
|
|
|
538
|
315
|
|
|
|
|
665
|
my @todrop=grep { ! defined $self->{'header_args'}->{$_} } keys %{$self->{'header_args'}}; |
|
779
|
|
|
|
|
1655
|
|
|
315
|
|
|
|
|
703
|
|
539
|
315
|
100
|
|
|
|
828
|
delete @{$self->{'header_args'}}{@todrop} if @todrop; |
|
1
|
|
|
|
|
3
|
|
540
|
|
|
|
|
|
|
|
541
|
315
|
|
|
|
|
1058
|
return $self->{'header_args'}; |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
############################################################################### |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
sub header_normalize ($$) { |
547
|
395
|
|
|
395
|
0
|
730
|
my ($self,$header)=@_; |
548
|
|
|
|
|
|
|
|
549
|
395
|
|
|
|
|
855
|
$header=lc($header); |
550
|
395
|
|
|
|
|
1311
|
$header=~s/-/_/g; |
551
|
395
|
|
|
|
|
1322
|
$header=~s/^_+//; |
552
|
|
|
|
|
|
|
|
553
|
395
|
|
|
|
|
1065
|
return $header; |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
############################################################################### |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
=item header_remove (@) |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
Remove one or more headers that were previously set in the same session. |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
$config->header_remove('X-Frame-Options'); |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=cut |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
sub header_remove ($@) { |
567
|
1
|
|
|
1
|
1
|
49
|
my $self=shift; |
568
|
|
|
|
|
|
|
|
569
|
1
|
|
|
|
|
1
|
delete @{$self->{'header_args'}}{map { $self->header_normalize($_) } @_}; |
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
3
|
|
570
|
|
|
|
|
|
|
|
571
|
1
|
|
|
|
|
9
|
return $self->{'header_args'}; |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
############################################################################### |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=item get_cookie ($;$) |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
Return cookie value for the given cookie name. Unless the second |
579
|
|
|
|
|
|
|
parameter is true, for cookies already set earlier in the same session |
580
|
|
|
|
|
|
|
it would return the value as set, not the value as it was originally |
581
|
|
|
|
|
|
|
received. |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
B The path and domain of cookies is ignored when checking for |
584
|
|
|
|
|
|
|
earlier set cookies and the last cookie stored with that name is |
585
|
|
|
|
|
|
|
returned! |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
=cut |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
sub get_cookie ($$;$) { |
590
|
54
|
|
|
54
|
1
|
14079
|
my ($self,$name,$original)=@_; |
591
|
|
|
|
|
|
|
|
592
|
54
|
50
|
33
|
|
|
282
|
if(!defined $name || !length($name)) { |
593
|
0
|
|
|
|
|
0
|
eprint "No cookie name given to ".ref($self)."::get_cookie()"; |
594
|
0
|
|
|
|
|
0
|
for(my $i=0; $i<3; ++$i) { |
595
|
0
|
0
|
|
|
|
0
|
dprint "..STACK: ".join('|',map { defined($_) ? $_ : '' } caller($i)); |
|
0
|
|
|
|
|
0
|
|
596
|
|
|
|
|
|
|
} |
597
|
0
|
|
|
|
|
0
|
return undef; |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
|
600
|
54
|
|
|
|
|
81
|
my $value; |
601
|
|
|
|
|
|
|
|
602
|
54
|
100
|
|
|
|
102
|
if(!$original) { |
603
|
40
|
100
|
|
|
|
53
|
foreach my $c (reverse @{$self->{'cookies'} || []}) { |
|
40
|
|
|
|
|
131
|
|
604
|
105
|
|
|
|
|
731
|
my $cookie=CGI::Cookie->new($c); |
605
|
|
|
|
|
|
|
|
606
|
105
|
100
|
|
|
|
28471
|
if($cookie->name() eq $name) { |
607
|
26
|
|
|
|
|
186
|
my $value=$cookie->value; |
608
|
|
|
|
|
|
|
|
609
|
26
|
|
|
|
|
166
|
my $expires_text=$cookie->expires; |
610
|
|
|
|
|
|
|
|
611
|
26
|
50
|
|
|
|
280
|
if($expires_text =~ /(\d{2})\W+([a-z]{3})\W+(\d{4})\W+(\d{2})\W+(\d{2})\W+(\d{2})/i) { |
612
|
26
|
|
|
|
|
86
|
my $midx=index('janfebmaraprmayjunjulaugsepoctnovdec',lc($2)); |
613
|
26
|
50
|
|
|
|
54
|
if($midx>=0) { |
614
|
26
|
|
|
|
|
44
|
$midx/=3; |
615
|
26
|
|
|
|
|
149
|
local($ENV{'TZ'})='UTC'; |
616
|
26
|
|
|
|
|
188
|
my $expires=mktime($6,$5,$4,$1,$midx,$3-1900); |
617
|
26
|
100
|
|
|
|
110
|
if($expires <= time) { |
618
|
4
|
|
|
|
|
16
|
$value=undef; |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
else { |
622
|
0
|
|
|
|
|
0
|
eprint "Invalid month '$2' in cookie '$name' expiration '$expires_text'"; |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
else { |
626
|
0
|
|
|
|
|
0
|
eprint "Invalid expiration '$expires_text' for cookie '$name'"; |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
|
629
|
26
|
|
|
|
|
141
|
return $value; |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
} |
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
|
634
|
28
|
|
|
|
|
86
|
my $cgi=$self->cgi; |
635
|
|
|
|
|
|
|
|
636
|
28
|
50
|
|
|
|
66
|
if(!$cgi) { |
637
|
0
|
|
|
|
|
0
|
eprint "Called get_cookie() before CGI is available"; |
638
|
0
|
|
|
|
|
0
|
return undef; |
639
|
|
|
|
|
|
|
} |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# When SameSite is set to None we might not get a value back from |
642
|
|
|
|
|
|
|
# incompatible browsers. See: |
643
|
|
|
|
|
|
|
# https://web.dev/samesite-cookie-recipes/ |
644
|
|
|
|
|
|
|
# https://www.chromium.org/updates/same-site/incompatible-clients |
645
|
|
|
|
|
|
|
# |
646
|
|
|
|
|
|
|
# Checking if 'sscompat' variant of this cookie is set, added by |
647
|
|
|
|
|
|
|
# add_cookie() code when configured. |
648
|
|
|
|
|
|
|
# |
649
|
28
|
|
|
|
|
49
|
$value=$self->cgi->cookie($name); |
650
|
28
|
100
|
|
|
|
30936
|
if(!defined $value) { |
651
|
7
|
|
|
|
|
19
|
my $defaults=$self->_cookie_config($name); |
652
|
7
|
100
|
|
|
|
21
|
if($defaults->{'-sscompat'}) { |
653
|
2
|
|
|
|
|
14
|
$value=$self->cgi->cookie($name.'-sscompat'); |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
|
657
|
28
|
|
|
|
|
7146
|
return $value; |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
############################################################################### |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=item new ($$) |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
Creates a new empty configuration object. |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=cut |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
sub new ($@) { |
669
|
38
|
|
|
38
|
1
|
1841
|
my $proto=shift; |
670
|
38
|
|
33
|
|
|
586
|
bless {},ref($proto) || $proto; |
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
############################################################################### |
674
|
|
|
|
|
|
|
1; |
675
|
|
|
|
|
|
|
__END__ |