| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Для разрешения upload-а нужно в директорию со скриптом поместить файл с |
|
2
|
|
|
|
|
|
|
# именем ".can_upload". В противном случае upload запрещаеся. |
|
3
|
|
|
|
|
|
|
# Если же этот файл задан, то делается попытка прочитать из него параметры: |
|
4
|
|
|
|
|
|
|
# dir=имя_директории для закачки |
|
5
|
|
|
|
|
|
|
# maxsize=максимальный_размер закачиваемого файла |
|
6
|
|
|
|
|
|
|
# В любом случае, по окончание работы скрипта закачанные файлы удаляются |
|
7
|
|
|
|
|
|
|
# (если только они не были перемещены скриптом в другое место). |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# TODO: |
|
10
|
|
|
|
|
|
|
# 3. Попробовать решить проблему экспорта |
|
11
|
|
|
|
|
|
|
# 4. Имена временных файлов должны быть совместимы с Маком. |
|
12
|
|
|
|
|
|
|
# 5. Поддержка undef в Serialize + предупреждения. |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
package CGI::WebIn; |
|
15
|
1
|
|
|
1
|
|
6640
|
use strict; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
473
|
|
|
16
|
|
|
|
|
|
|
our $VERSION = '2.03'; |
|
17
|
|
|
|
|
|
|
our @EXPORT=qw( |
|
18
|
|
|
|
|
|
|
%IN |
|
19
|
|
|
|
|
|
|
%GET |
|
20
|
|
|
|
|
|
|
%POST |
|
21
|
|
|
|
|
|
|
%COOKIES |
|
22
|
|
|
|
|
|
|
SetCookie |
|
23
|
|
|
|
|
|
|
DropCookie |
|
24
|
|
|
|
|
|
|
); |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
####################### Константы, управляющие работой ##################### |
|
28
|
|
|
|
|
|
|
our $CANUPL_FILE = ".can_upload"; # имя файла, разрешающего закачку |
|
29
|
|
|
|
|
|
|
our $MULTICHUNK_SIZE = 20000; # длина блока считывания STDIN-а |
|
30
|
|
|
|
|
|
|
our $MAX_ARRAY_IDX = 10000; # максимально возможный индекс N в a[N] |
|
31
|
|
|
|
|
|
|
our $uniq_tempnam = 0; # temp files counter |
|
32
|
|
|
|
|
|
|
our @TempFiles = (); # all temp files (to delete after end) |
|
33
|
|
|
|
|
|
|
our @Errors = (); # all query parsing errors |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# Настройки сериализации. |
|
36
|
|
|
|
|
|
|
# Некоторые внутренние настроечные переменные. Фактически, они используются |
|
37
|
|
|
|
|
|
|
# в качестве констант. Лучше всего их никогда не трогать. Эти константы |
|
38
|
|
|
|
|
|
|
# должны состоять из одного символа! |
|
39
|
|
|
|
|
|
|
our $Div1 = "."; # ALWAYS should be one nondigit!!! |
|
40
|
|
|
|
|
|
|
our $Div2 = "."; # may be the same as $Div1 |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
####################### Преременные с данными браузера ##################### |
|
44
|
|
|
|
|
|
|
our %IN = (); # Данные формы |
|
45
|
|
|
|
|
|
|
our %GET = (); # Данные GET |
|
46
|
|
|
|
|
|
|
our %POST = (); # Данные POST |
|
47
|
|
|
|
|
|
|
our %COOKIES = (); # Все пришедшие Cookies |
|
48
|
|
|
|
|
|
|
our %IMPORT_MOD = (); # Модули, затребовавшие импорт переменных (ключи) |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# void _reparseAll() |
|
52
|
|
|
|
|
|
|
# Parses all the input data. |
|
53
|
|
|
|
|
|
|
sub _reparseAll { |
|
54
|
1
|
50
|
|
1
|
|
5
|
if($ENV{QUERY_STRING}) { |
|
55
|
0
|
|
|
|
|
0
|
_parseURLEnc($ENV{QUERY_STRING},"get"); |
|
56
|
|
|
|
|
|
|
} |
|
57
|
1
|
50
|
|
|
|
5
|
if(uc($ENV{REQUEST_METHOD}) eq "POST") { |
|
58
|
0
|
0
|
0
|
|
|
0
|
if(exists($ENV{'CONTENT_TYPE'}) && $ENV{'CONTENT_TYPE'}=~m|^\s*multipart/form-data|i) { |
|
59
|
0
|
|
|
|
|
0
|
_parseMultipart(); |
|
60
|
|
|
|
|
|
|
} else { |
|
61
|
0
|
|
|
|
|
0
|
read(STDIN,my $data,$ENV{CONTENT_LENGTH}); |
|
62
|
0
|
|
|
|
|
0
|
_parseURLEnc($data,"post"); |
|
63
|
|
|
|
|
|
|
} |
|
64
|
|
|
|
|
|
|
} |
|
65
|
1
|
50
|
33
|
|
|
8
|
if($ENV{HTTP_COOKIE} || $ENV{COOKIE}) { |
|
66
|
0
|
|
|
|
|
0
|
_parseCookies(); |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
# use Data::Dumper; print "".Dumper(\%IN)." "; |
|
69
|
|
|
|
|
|
|
} |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# void import(...) |
|
73
|
|
|
|
|
|
|
# Called on 'use'. |
|
74
|
|
|
|
|
|
|
sub import |
|
75
|
1
|
|
|
1
|
|
7
|
{ my ($pkg, $opt)=@_; |
|
76
|
1
|
|
|
|
|
3
|
my $caller = caller(); |
|
77
|
1
|
50
|
|
|
|
3
|
export_vars($opt, $caller) if $opt; |
|
78
|
1
|
|
|
1
|
|
7
|
no strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
748
|
|
|
79
|
1
|
|
|
|
|
3
|
foreach (@EXPORT) { |
|
80
|
6
|
|
|
|
|
25
|
my ($type, $name) = /^([%@\$]?)(.*)$/s; |
|
81
|
6
|
100
|
|
|
|
19
|
if ($type eq '%') { |
|
|
|
50
|
|
|
|
|
|
|
82
|
4
|
|
|
|
|
4
|
*{$caller."::".$name} = \%{$name}; |
|
|
4
|
|
|
|
|
17
|
|
|
|
4
|
|
|
|
|
9
|
|
|
83
|
|
|
|
|
|
|
} elsif ($type eq '') { |
|
84
|
2
|
|
|
|
|
1
|
*{$caller."::".$name} = \&{$name}; |
|
|
2
|
|
|
|
|
1793
|
|
|
|
2
|
|
|
|
|
8
|
|
|
85
|
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# Deletes temporary files if present. |
|
91
|
|
|
|
|
|
|
sub END |
|
92
|
1
|
50
|
|
1
|
|
185
|
{ map { unlink($_) } @TempFiles if @TempFiles; |
|
|
0
|
|
|
|
|
0
|
|
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# list of string GetErrors() |
|
96
|
|
|
|
|
|
|
# Returns all errors collected while parsing the form input data |
|
97
|
|
|
|
|
|
|
# (for example, too large autoarray index). |
|
98
|
|
|
|
|
|
|
sub GetErrors { |
|
99
|
0
|
|
|
0
|
0
|
0
|
return @Errors; |
|
100
|
|
|
|
|
|
|
} |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# Encoding and decoding. |
|
104
|
1
|
|
|
1
|
0
|
309
|
sub URLEncode { my ($s)=@_; $s=~s{([^-_A-Za-z0-9./])}{sprintf("%%%02X", ord $1)}sge; return $s } |
|
|
1
|
|
|
|
|
11
|
|
|
|
233
|
|
|
|
|
750
|
|
|
|
1
|
|
|
|
|
5
|
|
|
105
|
1
|
|
|
1
|
0
|
6
|
sub URLDecode { my ($s)=@_; $s=~tr/+/ /; $s=~s/%([0-9A-Fa-f]{2})/chr(hex($1))/esg; return $s } |
|
|
1
|
|
|
|
|
6
|
|
|
|
1
|
|
|
|
|
6
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
1
|
|
|
|
|
2
|
|
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
my %CODE = ( |
|
109
|
|
|
|
|
|
|
'export_vars' => <<'END_OF_FUNC', |
|
110
|
|
|
|
|
|
|
# void export_vars(sting $options, string $toPkg) |
|
111
|
|
|
|
|
|
|
# Export EGPC-variables from %GET, %POST etc. |
|
112
|
|
|
|
|
|
|
sub export_vars |
|
113
|
|
|
|
|
|
|
{ my ($opt, $to)=@_; |
|
114
|
|
|
|
|
|
|
if(!scalar(@_)) { |
|
115
|
|
|
|
|
|
|
# Вызов без параметров - обойти и экспортировать во все модули-клиенты |
|
116
|
|
|
|
|
|
|
while(my ($mod,$opt)=each(%IMPORT_MOD)) { |
|
117
|
|
|
|
|
|
|
export_vars($opt,$mod); |
|
118
|
|
|
|
|
|
|
} |
|
119
|
|
|
|
|
|
|
} else { |
|
120
|
|
|
|
|
|
|
# Вызов с параметрами - экспорт переменных только в укакзанный модуль |
|
121
|
|
|
|
|
|
|
return if !$opt; |
|
122
|
|
|
|
|
|
|
$opt="gpces" if lc($opt) eq "a" || $opt eq "1"; |
|
123
|
|
|
|
|
|
|
# Сохраняем информацию о том, что модуль "хочет" экспортирования и |
|
124
|
|
|
|
|
|
|
# в дальнейшем. Например, при вызове SetCookie() соответствующая |
|
125
|
|
|
|
|
|
|
# переменная создастся не только в %COOKIES, но и во всех модулях. |
|
126
|
|
|
|
|
|
|
$IMPORT_MOD{$to}=$opt; |
|
127
|
|
|
|
|
|
|
# Экспортируем еще не существующие переменные |
|
128
|
|
|
|
|
|
|
no strict; |
|
129
|
|
|
|
|
|
|
my $Bad=\%{$to."::"}; |
|
130
|
|
|
|
|
|
|
foreach my $op (split //,$opt) { |
|
131
|
|
|
|
|
|
|
$op=lc($op); |
|
132
|
|
|
|
|
|
|
my $Hash = |
|
133
|
|
|
|
|
|
|
$op eq "g" && \%GET || |
|
134
|
|
|
|
|
|
|
$op eq "p" && \%POST || |
|
135
|
|
|
|
|
|
|
$op eq "c" && \%COOKIES || |
|
136
|
|
|
|
|
|
|
$op eq "e" && \%ENV || next; |
|
137
|
|
|
|
|
|
|
while(my ($k,$v)=each(%$Hash)) { |
|
138
|
|
|
|
|
|
|
# не переписывать существующие переменные |
|
139
|
|
|
|
|
|
|
next if exists $Bad->{$k}; |
|
140
|
|
|
|
|
|
|
## BUGFIX 11.07.2002 v1.10: |
|
141
|
|
|
|
|
|
|
## разрешается применять только буквенно-цифровые имена, |
|
142
|
|
|
|
|
|
|
## раньше имена вида SomeModule::var приводили к дыре |
|
143
|
|
|
|
|
|
|
## в безопасности. |
|
144
|
|
|
|
|
|
|
next if $k=~/[^\w\d_]/s; |
|
145
|
|
|
|
|
|
|
*{$to."::".$k}=ref($v)? $Hash->{$k} : \$Hash->{$k}; |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
END_OF_FUNC |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
'_processPar' => <<'END_OF_FUNC', |
|
154
|
|
|
|
|
|
|
# void _processPar(string $key, string $value, string $type) |
|
155
|
|
|
|
|
|
|
# Добавляет пару $key=>$value в хэш %IN (с разбором многоуровневых хэшей), |
|
156
|
|
|
|
|
|
|
# а также в хэш %GET, %POST или %COOKIES, в зависимости от значения $type |
|
157
|
|
|
|
|
|
|
# (get, post, cookies соответственно). |
|
158
|
|
|
|
|
|
|
# Пустые скобки "{}" заменяются на значение "{$v}"! |
|
159
|
|
|
|
|
|
|
sub _processPar |
|
160
|
|
|
|
|
|
|
{ my ($k,$v,$type)=@_; |
|
161
|
|
|
|
|
|
|
return if !defined($k); |
|
162
|
|
|
|
|
|
|
$type=uc($type||"IN"); |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
## BUGFIX 12.07.2002 v1.10: |
|
165
|
|
|
|
|
|
|
## до этого было s/\r//sg, что неправильно работало на Маке. |
|
166
|
|
|
|
|
|
|
$v=~s/\x0d\x0a?|\x0a\x0d?/\n/sg if defined $v && !ref $v; |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# Проверяем вид "a{10}{20}" и заодно получаем первый ключ |
|
169
|
|
|
|
|
|
|
do { push @Errors, "$type: Unknown input field format '$k'"; return } if $k!~/^([^}{\[\]]+)/sg; |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
## Этап I: Получаем все индексы, обрамленные соотв. скобками. |
|
172
|
|
|
|
|
|
|
my @Ind = ([$1, '']); |
|
173
|
|
|
|
|
|
|
while(pos $k < length $k) { |
|
174
|
|
|
|
|
|
|
my ($i,$t); |
|
175
|
|
|
|
|
|
|
$k=~/\G |
|
176
|
|
|
|
|
|
|
\{ ( |
|
177
|
|
|
|
|
|
|
(?: |
|
178
|
|
|
|
|
|
|
[^}"']* | |
|
179
|
|
|
|
|
|
|
## BUGFIX 12.07.2002 v1.10: |
|
180
|
|
|
|
|
|
|
## нужно писать [^"\\], а не [^"]. |
|
181
|
|
|
|
|
|
|
## after slash ALWAYS must be any character. |
|
182
|
|
|
|
|
|
|
"(?:[^"\\]+|\\.)*" | |
|
183
|
|
|
|
|
|
|
'(?:[^'\\]+|\\.)*' |
|
184
|
|
|
|
|
|
|
) |
|
185
|
|
|
|
|
|
|
) \} |
|
186
|
|
|
|
|
|
|
/sxgc and do { $t=$Ind[-1][1]='HASH'; push @Ind, $i=[$1, ''] } |
|
187
|
|
|
|
|
|
|
or #### |
|
188
|
|
|
|
|
|
|
$k=~/\G |
|
189
|
|
|
|
|
|
|
\[ ( |
|
190
|
|
|
|
|
|
|
(?: |
|
191
|
|
|
|
|
|
|
[^]"']* | |
|
192
|
|
|
|
|
|
|
"(?:[^"\\]+|\\.)*" | |
|
193
|
|
|
|
|
|
|
'(?:[^'\\]+|\\.)*' |
|
194
|
|
|
|
|
|
|
)* |
|
195
|
|
|
|
|
|
|
) \] |
|
196
|
|
|
|
|
|
|
/sxgc and do { $t=$Ind[-1][1]='ARRAY'; push @Ind, $i=[$1, ''] } |
|
197
|
|
|
|
|
|
|
or ### |
|
198
|
|
|
|
|
|
|
do { push @Errors, "$type: Corrupted parameter '$k'"; return }; |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
if($i->[0] eq "" && defined $v) { |
|
201
|
|
|
|
|
|
|
# Заменяем пустой индекс БЕЗ КАВЫЧЕК на $v или -1 |
|
202
|
|
|
|
|
|
|
$i->[0] = $t eq 'HASH'? $v : ''; |
|
203
|
|
|
|
|
|
|
} else { |
|
204
|
|
|
|
|
|
|
# Убираем слэши перед кавычками, но только если строка была заковычена. |
|
205
|
|
|
|
|
|
|
$i->[0]=~s/^(['"])(.*)\1$/$2/sg |
|
206
|
|
|
|
|
|
|
and |
|
207
|
|
|
|
|
|
|
$i->[0]=~s/\\(['"\\])/$1/sg; |
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
# [0] содержит очередной индекс. |
|
211
|
|
|
|
|
|
|
# [1] содержит ТИП объекта, в котором этот индекс СОДЕРЖИТСЯ. |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# use Data::Dumper; print "".Dumper(\%IN)." "; |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
## Этап II: заполняем хэши. К сожалению, приходится делать |
|
216
|
|
|
|
|
|
|
## цикл по всем хэшам и фактически делать одну работу несколько |
|
217
|
|
|
|
|
|
|
## раз, потому что где-то уже могут существовать полузаполненные |
|
218
|
|
|
|
|
|
|
## хэши. |
|
219
|
|
|
|
|
|
|
my @Outs=(\%IN); |
|
220
|
|
|
|
|
|
|
push @Outs, \%GET if lc($type) eq "get"; |
|
221
|
|
|
|
|
|
|
push @Outs, \%POST if lc($type) eq "post"; |
|
222
|
|
|
|
|
|
|
push @Outs, \%COOKIES if lc($type) eq "cookie"; |
|
223
|
|
|
|
|
|
|
foreach my $cur (@Outs) { |
|
224
|
|
|
|
|
|
|
foreach my $idx (@Ind) { |
|
225
|
|
|
|
|
|
|
# Текущий ключ и тип значения по этому ключу. |
|
226
|
|
|
|
|
|
|
my ($i,$t) = @$idx; |
|
227
|
|
|
|
|
|
|
# Получаем ссылку $r не то место, куда нужно записать значение. |
|
228
|
|
|
|
|
|
|
my $r; |
|
229
|
|
|
|
|
|
|
if(ref $cur eq 'HASH') { |
|
230
|
|
|
|
|
|
|
# Работаем с $cur как с хэшем. |
|
231
|
|
|
|
|
|
|
$r = \$cur->{$i}; |
|
232
|
|
|
|
|
|
|
} elsif(ref $cur eq 'ARRAY') { |
|
233
|
|
|
|
|
|
|
# Работаем с $cur как с массивом. |
|
234
|
|
|
|
|
|
|
# Индекс -1 означает "добавить в конец". |
|
235
|
|
|
|
|
|
|
$i = @$cur if $i eq ""; |
|
236
|
|
|
|
|
|
|
# Не-цифровые и слишком большие индексы не допускаются. |
|
237
|
|
|
|
|
|
|
do { push @Errors, "$type: Non-numeric index '$i' in '$k'"; return } if $i=~/[^\d]/s; |
|
238
|
|
|
|
|
|
|
do { push @Errors, "$type: Too large index '$i' in '$k'"; return } if $i>$MAX_ARRAY_IDX; |
|
239
|
|
|
|
|
|
|
$r = \$cur->[$i]; |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
# Если такого ключа еще нет, то в $$r будет undef. |
|
242
|
|
|
|
|
|
|
$$r = ($t eq 'HASH'? {} : $t eq 'ARRAY'? [] : '') if !defined $$r; |
|
243
|
|
|
|
|
|
|
# Проверка соответствия типов. |
|
244
|
|
|
|
|
|
|
if(ref($$r) ne $t) { |
|
245
|
|
|
|
|
|
|
push @Errors, "$type: Mismatched parameter type: key '$i' in '$k' later defined as ".(ref($$r)||"SCALAR").", not ".(!$t? "SCALAR" : $t eq 'HASH'? 'HASH' : 'ARRAY'); |
|
246
|
|
|
|
|
|
|
return; |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
$$r = $v if !$t; |
|
249
|
|
|
|
|
|
|
$cur = $$r; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
} |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
END_OF_FUNC |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
'_parseURLEnc' => <<'END_OF_FUNC', |
|
257
|
|
|
|
|
|
|
# void _parseURLEnc(string $input, string $type) |
|
258
|
|
|
|
|
|
|
sub _parseURLEnc |
|
259
|
|
|
|
|
|
|
{ my ($tosplit,$type) = @_; |
|
260
|
|
|
|
|
|
|
my (@pairs) = split(/[&?]/,$tosplit); |
|
261
|
|
|
|
|
|
|
my ($param,$value); |
|
262
|
|
|
|
|
|
|
foreach (@pairs) { |
|
263
|
|
|
|
|
|
|
($param,$value) = split('=',$_,2); |
|
264
|
|
|
|
|
|
|
$param = URLDecode(defined($param)?$param:""); |
|
265
|
|
|
|
|
|
|
$value = URLDecode(defined($value)?$value:""); |
|
266
|
|
|
|
|
|
|
_processPar($param,$value,$type); |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
END_OF_FUNC |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
'tempnam' => <<'END_OF_FUNC', |
|
273
|
|
|
|
|
|
|
# string tempnam([string $Dir]) |
|
274
|
|
|
|
|
|
|
# Возвращает уникальное (используется PID и таймер) имя файла в директории, указанной в |
|
275
|
|
|
|
|
|
|
# параметрах. По умолчанию - в директории, указанной в переменной окружения TMP или TEMP, |
|
276
|
|
|
|
|
|
|
# или, в крайнем случае, в текущей. В конце работы скрипта все файлы, имеющие |
|
277
|
|
|
|
|
|
|
# имена, сгенерированные tempnam(), будут удалены! |
|
278
|
|
|
|
|
|
|
# Всегда возвращает полный путь к временному файлу. |
|
279
|
|
|
|
|
|
|
sub tempnam |
|
280
|
|
|
|
|
|
|
{ my ($dir)=@_; |
|
281
|
|
|
|
|
|
|
foreach my $cd ($dir,$ENV{TMP},$ENV{TEMP},"/tmp",".") { |
|
282
|
|
|
|
|
|
|
if(defined $cd && -d $cd && -w $cd) { $dir=$cd; last; } |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
my $nm=$dir."/".time()."-$$-".(++$uniq_tempnam).".tmp"; |
|
285
|
|
|
|
|
|
|
if($nm!~m{^[/\\]}) { |
|
286
|
|
|
|
|
|
|
require Cwd; |
|
287
|
|
|
|
|
|
|
$nm=Cwd::getcwd()."/".$nm; |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
push(@TempFiles,$nm); |
|
290
|
|
|
|
|
|
|
return $nm; |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
END_OF_FUNC |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
'_parseMultipart' => <<'END_OF_FUNC', |
|
296
|
|
|
|
|
|
|
# hash _readUplConf() |
|
297
|
|
|
|
|
|
|
# Читает конфигурационный файл, разрешающий или запрещающий |
|
298
|
|
|
|
|
|
|
# закачку в текущей директории. |
|
299
|
|
|
|
|
|
|
sub _readUplConf { |
|
300
|
|
|
|
|
|
|
open(local *F,"<$CANUPL_FILE") or return |
|
301
|
|
|
|
|
|
|
my %cfg=(); |
|
302
|
|
|
|
|
|
|
while(my $st=) { |
|
303
|
|
|
|
|
|
|
$st=~s/^\s+|\s+$|#.*$//gs; |
|
304
|
|
|
|
|
|
|
next if $st eq ""; |
|
305
|
|
|
|
|
|
|
my ($k,$v)=split(/=/,$st,2); |
|
306
|
|
|
|
|
|
|
$cfg{$k}=$v; |
|
307
|
|
|
|
|
|
|
} |
|
308
|
|
|
|
|
|
|
return %cfg; |
|
309
|
|
|
|
|
|
|
} |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# Обработка Multipart-данных формы |
|
312
|
|
|
|
|
|
|
# void _parseMultipart() |
|
313
|
|
|
|
|
|
|
our ($InBuf, $InLength); # our для strict |
|
314
|
|
|
|
|
|
|
sub _parseMultipart |
|
315
|
|
|
|
|
|
|
{ # Устанавливаем директорию и другие параметры для закачки (если разрешена) |
|
316
|
|
|
|
|
|
|
my %cfg=_readUplConf(); # свойства закачки |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
#------- Работа с STDIN с возможностью "запихивания" данных обратно в поток |
|
319
|
|
|
|
|
|
|
$InBuf=""; $InLength=$ENV{CONTENT_LENGTH}; |
|
320
|
|
|
|
|
|
|
sub _readInput { |
|
321
|
|
|
|
|
|
|
my $sz=shift||$InLength; |
|
322
|
|
|
|
|
|
|
my $need=$MULTICHUNK_SIZE>$sz? $sz : $MULTICHUNK_SIZE; |
|
323
|
|
|
|
|
|
|
my $nBuf=length($InBuf)<$need? length($InBuf) : $need; |
|
324
|
|
|
|
|
|
|
my $out=substr($InBuf,0,$nBuf); $InBuf=substr($InBuf,$nBuf); |
|
325
|
|
|
|
|
|
|
read(STDIN,$out,$need-$nBuf,$nBuf) if $need-$nBuf>0; |
|
326
|
|
|
|
|
|
|
$InLength-=length($out); |
|
327
|
|
|
|
|
|
|
return $out; |
|
328
|
|
|
|
|
|
|
} |
|
329
|
|
|
|
|
|
|
sub _putBack |
|
330
|
|
|
|
|
|
|
{ my ($data)=@_; |
|
331
|
|
|
|
|
|
|
$InBuf=$data.$InBuf; |
|
332
|
|
|
|
|
|
|
$InLength+=length($data); |
|
333
|
|
|
|
|
|
|
} |
|
334
|
|
|
|
|
|
|
sub _isEof { return !$InLength; } |
|
335
|
|
|
|
|
|
|
#-------- Конец внутренних функций |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
binmode(STDIN); |
|
338
|
|
|
|
|
|
|
# Сначала читаем разделитель и финальные "\r\n" |
|
339
|
|
|
|
|
|
|
my ($bound,$CRLF) = _readInput()=~/(^[^\r\n]*)([\r\n]*)(.*)/s; # Выделяем разделитель БЕЗ \n |
|
340
|
|
|
|
|
|
|
_putBack($3); |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
# Теперь читаем записи, завершенные разделителем |
|
343
|
|
|
|
|
|
|
while((my $Data=_readInput()) ne "") { |
|
344
|
|
|
|
|
|
|
if(substr($Data,0,2) eq "--") { last; } # Проверяем, не конец ли это |
|
345
|
|
|
|
|
|
|
# Выделяем ВСЕ строки заголовка (до пустой строки). |
|
346
|
|
|
|
|
|
|
$Data=~/^[$CRLF]*(.*?)$CRLF$CRLF(.*)/s |
|
347
|
|
|
|
|
|
|
or do { push @Errors, "Malformed multipart header"; return }; |
|
348
|
|
|
|
|
|
|
_putBack($2); # Остаток запихиваем обратно |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# Получаем заголовки записи в %Headers |
|
351
|
|
|
|
|
|
|
my @Lines=split(/$CRLF/,$1); # строки заголовка |
|
352
|
|
|
|
|
|
|
my %Headers=(); |
|
353
|
|
|
|
|
|
|
foreach my $st (@Lines) { |
|
354
|
|
|
|
|
|
|
my ($k,$v)=split(/: */,$st,2); |
|
355
|
|
|
|
|
|
|
$Headers{lc($k)}=$v; |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
if(!%Headers) { push @Errors, "Malformed multipart POST (no header)"; return; } |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
# Выделяем имя тэга и имя файла (если задано) |
|
360
|
|
|
|
|
|
|
my ($name)=$Headers{'content-disposition'}=~/\bname="?([^\";]*)"?/; |
|
361
|
|
|
|
|
|
|
my ($filename) = $Headers{'content-disposition'}=~/\bfilename="?([^\";]*)"?/; |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
# Если это не закачка, то читаем данные и продолжаем |
|
364
|
|
|
|
|
|
|
if(!defined $filename || $filename eq "") { |
|
365
|
|
|
|
|
|
|
my ($body,$i); |
|
366
|
|
|
|
|
|
|
$body = ""; |
|
367
|
|
|
|
|
|
|
for($body=""; ($i=index($body,$bound))<0 && !_isEof(); ) { |
|
368
|
|
|
|
|
|
|
$body.=_readInput(); |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
if($i<0) { push @Errors, "Malformed multipart POST (no boundary after body)"; return; } |
|
371
|
|
|
|
|
|
|
_putBack(substr($body,$i+length($bound))); # запихиваем остаток назад |
|
372
|
|
|
|
|
|
|
_processPar($name,substr($body,0,$i-length($CRLF)),"post"); |
|
373
|
|
|
|
|
|
|
next; |
|
374
|
|
|
|
|
|
|
} |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# Иначе это закачка. Записываем временный файл. |
|
377
|
|
|
|
|
|
|
my $temp=defined $cfg{dir}? tempnam($cfg{dir}):tempnam(); |
|
378
|
|
|
|
|
|
|
local *F; open(F,">$temp") or die("Cannot open temporary file $temp"); binmode(F); |
|
379
|
|
|
|
|
|
|
my $written=0; # сколько байт в файле |
|
380
|
|
|
|
|
|
|
my $stopWrite=0; # нужно ли записывать, или пропускать |
|
381
|
|
|
|
|
|
|
while(1) { |
|
382
|
|
|
|
|
|
|
# Файл слишком велик или же закачка запрещена?.. |
|
383
|
|
|
|
|
|
|
$stopWrite ||= |
|
384
|
|
|
|
|
|
|
!%cfg && "File not found: $CANUPL_FILE" |
|
385
|
|
|
|
|
|
|
|| (defined $cfg{maxsize} && $written>$cfg{maxsize}) && "File exceeds limit of $cfg{maxsize} bytes"; |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
my $body1=_readInput(); |
|
388
|
|
|
|
|
|
|
my $body2=_readInput(128); # для проверки разделителя длиной <128 байт |
|
389
|
|
|
|
|
|
|
my $body=$body1.$body2; |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# Нашли конец файла (разделитель)? |
|
392
|
|
|
|
|
|
|
if((my $i=index($body,$bound))>=0) { |
|
393
|
|
|
|
|
|
|
$written+=$i-length($CRLF); |
|
394
|
|
|
|
|
|
|
print F substr($body,0,$i-length($CRLF)) if !$stopWrite; |
|
395
|
|
|
|
|
|
|
_putBack(substr($body,$i+length($bound))); |
|
396
|
|
|
|
|
|
|
last; |
|
397
|
|
|
|
|
|
|
} |
|
398
|
|
|
|
|
|
|
$written+=length($body1); |
|
399
|
|
|
|
|
|
|
print F $body1 if !$stopWrite; |
|
400
|
|
|
|
|
|
|
_putBack($body2); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
close(F); |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# Формируем значение параметра. |
|
405
|
|
|
|
|
|
|
## BUGFIX 13.07.2002: |
|
406
|
|
|
|
|
|
|
## раньше имя вида f[] и f{} приводило к неправильному |
|
407
|
|
|
|
|
|
|
## созданию этого хэша, т.к. было насколько вызовов |
|
408
|
|
|
|
|
|
|
## _processPar с суффиксами {filename}, {file} и т.д. |
|
409
|
|
|
|
|
|
|
my %hash=(); |
|
410
|
|
|
|
|
|
|
$hash{filename}=$filename; |
|
411
|
|
|
|
|
|
|
# Файл слишком большой, либо upload запрещен?.. |
|
412
|
|
|
|
|
|
|
if($stopWrite) { |
|
413
|
|
|
|
|
|
|
unlink($temp); |
|
414
|
|
|
|
|
|
|
$hash{aborted}=$stopWrite; |
|
415
|
|
|
|
|
|
|
} else { |
|
416
|
|
|
|
|
|
|
# Иначе все в порядке |
|
417
|
|
|
|
|
|
|
$hash{headers}=\%Headers; |
|
418
|
|
|
|
|
|
|
$hash{file}=$temp; |
|
419
|
|
|
|
|
|
|
$hash{size}=-s $temp; |
|
420
|
|
|
|
|
|
|
$hash{type}=$Headers{'content-type'} if $Headers{'content-type'}; |
|
421
|
|
|
|
|
|
|
} |
|
422
|
|
|
|
|
|
|
# Добавляем параметр. |
|
423
|
|
|
|
|
|
|
_processPar($name,\%hash,"post"); |
|
424
|
|
|
|
|
|
|
} |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
END_OF_FUNC |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
'_parseCookies' => <<'END_OF_FUNC', |
|
430
|
|
|
|
|
|
|
# Разбирает пришедшие cookies |
|
431
|
|
|
|
|
|
|
sub _parseCookies |
|
432
|
|
|
|
|
|
|
{ my @Pairs = split("; *",$ENV{HTTP_COOKIE} || $ENV{COOKIE} || ""); |
|
433
|
|
|
|
|
|
|
foreach (@Pairs) { |
|
434
|
|
|
|
|
|
|
my ($key,$value); |
|
435
|
|
|
|
|
|
|
if(/^([^=]+)=(.*)/) { $key = $1; $value = $2; } else { $key = $_; $value = ''; } |
|
436
|
|
|
|
|
|
|
$key=URLDecode($key); |
|
437
|
|
|
|
|
|
|
$value=URLDecode($value); |
|
438
|
|
|
|
|
|
|
my $v=Unserialize($value); |
|
439
|
|
|
|
|
|
|
$value=defined($v)?$v:$value; |
|
440
|
|
|
|
|
|
|
_processPar($key,$value,"cookie"); |
|
441
|
|
|
|
|
|
|
} |
|
442
|
|
|
|
|
|
|
} |
|
443
|
|
|
|
|
|
|
END_OF_FUNC |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
'ExpireCalc' => <<'END_OF_FUNC', |
|
447
|
|
|
|
|
|
|
# int ExpireCalc(string $tm) |
|
448
|
|
|
|
|
|
|
# This routine creates an expires time exactly some number of |
|
449
|
|
|
|
|
|
|
# hours from the current time. It incorporates modifications from Mark Fisher. |
|
450
|
|
|
|
|
|
|
# Format for time $tm can be in any of the forms... |
|
451
|
|
|
|
|
|
|
# "now" -- expire immediately |
|
452
|
|
|
|
|
|
|
# "+180s" -- in 180 seconds |
|
453
|
|
|
|
|
|
|
# "+2m" -- in 2 minutes |
|
454
|
|
|
|
|
|
|
# "+12h" -- in 12 hours |
|
455
|
|
|
|
|
|
|
# "+1d" -- in 1 day |
|
456
|
|
|
|
|
|
|
# "+3M" -- in 3 months |
|
457
|
|
|
|
|
|
|
# "+2y" -- in 2 years |
|
458
|
|
|
|
|
|
|
# "-3m" -- 3 minutes ago(!) |
|
459
|
|
|
|
|
|
|
sub ExpireCalc |
|
460
|
|
|
|
|
|
|
{ my($time)=@_; |
|
461
|
|
|
|
|
|
|
my(%mult)=('s'=>1, 'm'=>60, 'h'=>60*60, 'd'=>60*60*24, 'M'=>60*60*24*30, 'y'=>60*60*24*365); |
|
462
|
|
|
|
|
|
|
my($offset); |
|
463
|
|
|
|
|
|
|
if(lc($time) eq 'now') { $offset = 0; } |
|
464
|
|
|
|
|
|
|
elsif($time=~/^([+-](?:\d+|\d*\.\d*))([mhdMy]?)/) { $offset = ($mult{$2} || 1)*$1; } |
|
465
|
|
|
|
|
|
|
else { return $time; } |
|
466
|
|
|
|
|
|
|
return (time+$offset); |
|
467
|
|
|
|
|
|
|
} |
|
468
|
|
|
|
|
|
|
END_OF_FUNC |
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
'Expires' => <<'END_OF_FUNC', |
|
472
|
|
|
|
|
|
|
# int Expires(int $time, string $format) |
|
473
|
|
|
|
|
|
|
# This internal routine creates date strings suitable for use in |
|
474
|
|
|
|
|
|
|
# cookies ($format="cookie") and HTTP headers ($format="http" or nothing). |
|
475
|
|
|
|
|
|
|
# (They differ, unfortunately.) Thanks to Fisher Mark for this. |
|
476
|
|
|
|
|
|
|
sub Expires |
|
477
|
|
|
|
|
|
|
{ my($time,$format) = @_; $format ||= 'http'; |
|
478
|
|
|
|
|
|
|
my(@MON)=qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/; |
|
479
|
|
|
|
|
|
|
my(@WDAY) = qw/Sun Mon Tue Wed Thu Fri Sat/; |
|
480
|
|
|
|
|
|
|
# pass through preformatted dates for the sake of expire_calc() |
|
481
|
|
|
|
|
|
|
$time = ExpireCalc($time); return $time unless $time =~ /^\d+$/; |
|
482
|
|
|
|
|
|
|
# cookies use '-' as date separator, HTTP uses ' ' |
|
483
|
|
|
|
|
|
|
my($sc) = ' '; $sc = '-' if $format eq "cookie"; |
|
484
|
|
|
|
|
|
|
my($sec,$min,$hour,$mday,$mon,$year,$wday) = gmtime($time); |
|
485
|
|
|
|
|
|
|
return sprintf("%s, %02d$sc%s$sc%04d %02d:%02d:%02d GMT", |
|
486
|
|
|
|
|
|
|
$WDAY[$wday],$mday,$MON[$mon],$year+1900,$hour,$min,$sec); |
|
487
|
|
|
|
|
|
|
} |
|
488
|
|
|
|
|
|
|
END_OF_FUNC |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
'SetCookie' => <<'END_OF_FUNC', |
|
492
|
|
|
|
|
|
|
# void SetCookie(string $name, string $value [,int $expire][,$path][,$domain][bool $secure]) |
|
493
|
|
|
|
|
|
|
# Устанавливает cookie с именем $name и значение $value ($value может быть сложным объектом |
|
494
|
|
|
|
|
|
|
# - в частности, ссылкой на массив или хэш). |
|
495
|
|
|
|
|
|
|
# Если $value не задан (undef), cookie удаляется. |
|
496
|
|
|
|
|
|
|
# Если $expire не задан, время жизни становится бесконечным. Если задан, но равен |
|
497
|
|
|
|
|
|
|
# нулю - создается one-session cookie. |
|
498
|
|
|
|
|
|
|
# Параметр $expire можно задавать в виде, который "понимает" функция ExpireCalc(). |
|
499
|
|
|
|
|
|
|
sub SetCookie |
|
500
|
|
|
|
|
|
|
{ my ($name,$value,$expires,$path,$domain,$secure)=@_; |
|
501
|
|
|
|
|
|
|
my $NeedDel=0; |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# [12.03.2002] Можно и без этого. |
|
504
|
|
|
|
|
|
|
# if(!defined $path) { |
|
505
|
|
|
|
|
|
|
# $path=$ENV{SCRIPT_NAME}; |
|
506
|
|
|
|
|
|
|
# $path=~s{/[^/]*$}{}sg; |
|
507
|
|
|
|
|
|
|
# $path.="/"; |
|
508
|
|
|
|
|
|
|
# } |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
if(!defined $expires) { $expires="+20y"; } |
|
511
|
|
|
|
|
|
|
if(!defined $value) { $value=""; $expires="-3y"; $NeedDel=1; } |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
my @Param; |
|
514
|
|
|
|
|
|
|
push(@Param,URLEncode($name)."=".URLEncode(Serialize($value))); |
|
515
|
|
|
|
|
|
|
push(@Param,"domain=$domain") if defined $domain; |
|
516
|
|
|
|
|
|
|
push(@Param,"path=$path") if defined $path; |
|
517
|
|
|
|
|
|
|
push(@Param,"expires=".Expires($expires,"cookie")) if $expires; |
|
518
|
|
|
|
|
|
|
push(@Param,'secure') if $secure; |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
my $cook="Set-Cookie: ".join("; ",@Param); |
|
521
|
|
|
|
|
|
|
eval { |
|
522
|
|
|
|
|
|
|
local ($SIG{__WARN__},$SIG{__DIE__})=(sub {}, sub {}); |
|
523
|
|
|
|
|
|
|
require CGI::WebOut; |
|
524
|
|
|
|
|
|
|
}; |
|
525
|
|
|
|
|
|
|
if($@) { |
|
526
|
|
|
|
|
|
|
# Если не вышло загрузить CGI::WebOut, то просто печатаем. |
|
527
|
|
|
|
|
|
|
print $cook . "\r\n"; |
|
528
|
|
|
|
|
|
|
} else { |
|
529
|
|
|
|
|
|
|
CGI::WebOut::Header($cook); |
|
530
|
|
|
|
|
|
|
} |
|
531
|
|
|
|
|
|
|
if(!$NeedDel) { _processPar($name,$value,"cookie"); } |
|
532
|
|
|
|
|
|
|
else { _processPar($name,undef,"cookie"); } |
|
533
|
|
|
|
|
|
|
# Экспортируем Cookie во все нужные модули |
|
534
|
|
|
|
|
|
|
export_vars(); |
|
535
|
|
|
|
|
|
|
} |
|
536
|
|
|
|
|
|
|
END_OF_FUNC |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
'DropCookie' => <<'END_OF_FUNC', |
|
540
|
|
|
|
|
|
|
# void DropCookie(string $name [,$path] [,$domain]) |
|
541
|
|
|
|
|
|
|
# Удаляет cookie с именем $name. Параметры $path и $domain |
|
542
|
|
|
|
|
|
|
# должны точно совпадать с теми, которые были заданы при |
|
543
|
|
|
|
|
|
|
# установке Cookie. |
|
544
|
|
|
|
|
|
|
sub DropCookie |
|
545
|
|
|
|
|
|
|
{ my ($name,$path,$domain)=@_; |
|
546
|
|
|
|
|
|
|
SetCookie($name,undef,undef,$path,$domain); |
|
547
|
|
|
|
|
|
|
} |
|
548
|
|
|
|
|
|
|
END_OF_FUNC |
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
'Serialize' => <<'END_OF_FUNC', |
|
552
|
|
|
|
|
|
|
# string Serialize(mixed @args) |
|
553
|
|
|
|
|
|
|
# Упаковывает в строку любой (практически) объект. Так что не обязательно передавать |
|
554
|
|
|
|
|
|
|
# этой функции ссылку - можно прямо объект целиком. |
|
555
|
|
|
|
|
|
|
# (В этом случае он будет рассмотрен как список). |
|
556
|
|
|
|
|
|
|
# Нельзя упаковывать объекты, содержащие ссылки на функции и дескрипторы файлов. |
|
557
|
|
|
|
|
|
|
# В случае ошибки возвращает undef и выводит warning. |
|
558
|
|
|
|
|
|
|
sub Serialize |
|
559
|
|
|
|
|
|
|
{ my $st="L".($#_+1).$Div2; |
|
560
|
|
|
|
|
|
|
foreach my $Arg (@_) { |
|
561
|
|
|
|
|
|
|
while((my $Ref=ref($Arg)) eq "REF") { $st.="r"; $Arg=$$Arg; } |
|
562
|
|
|
|
|
|
|
if(ref($Arg) ne "") { $st.="r"; } |
|
563
|
|
|
|
|
|
|
if(ref($Arg) eq "") { $st.=length($Arg).$Div1.$Arg; } |
|
564
|
|
|
|
|
|
|
elsif(ref($Arg) eq "SCALAR") { $st.=length($$Arg).$Div1.$$Arg; } |
|
565
|
|
|
|
|
|
|
elsif(ref($Arg) eq "ARRAY") { $st.=Serialize(@$Arg); } |
|
566
|
|
|
|
|
|
|
elsif(ref($Arg) eq "HASH") { $st.="H".Serialize(%$Arg); } |
|
567
|
|
|
|
|
|
|
else { warn("Serialize: invalid field type '".ref($Arg)."'"); return undef; } |
|
568
|
|
|
|
|
|
|
} |
|
569
|
|
|
|
|
|
|
return $st; |
|
570
|
|
|
|
|
|
|
} |
|
571
|
|
|
|
|
|
|
END_OF_FUNC |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
'Unserialize' => <<'END_OF_FUNC', |
|
575
|
|
|
|
|
|
|
# mixed _Unserialize(string $st) |
|
576
|
|
|
|
|
|
|
# Internal function. |
|
577
|
|
|
|
|
|
|
sub _Unserialize |
|
578
|
|
|
|
|
|
|
{ my ($st,$TotalLen)=@_; |
|
579
|
|
|
|
|
|
|
# Считаем число ссылок |
|
580
|
|
|
|
|
|
|
my $RefCount; |
|
581
|
|
|
|
|
|
|
for($RefCount=0; substr($st,$RefCount,1) eq "r"; $RefCount++) {;} |
|
582
|
|
|
|
|
|
|
$$TotalLen+=$RefCount; $st=substr($st,$RefCount); |
|
583
|
|
|
|
|
|
|
# Определяем тип |
|
584
|
|
|
|
|
|
|
my $Type="S"; # Может быть еще: "HL" (да, 2 символа!!!) или "L" |
|
585
|
|
|
|
|
|
|
if(substr($st,0,1) eq "H") { $Type="H"; $st=substr($st,2); $$TotalLen+=2; } |
|
586
|
|
|
|
|
|
|
elsif(substr($st,0,1) eq "L") { $Type="L"; $$TotalLen++; $st=substr($st,1); } |
|
587
|
|
|
|
|
|
|
# Выполняем действия в зваисимости от типа |
|
588
|
|
|
|
|
|
|
my $PResult; |
|
589
|
|
|
|
|
|
|
if($Type eq "S") { |
|
590
|
|
|
|
|
|
|
# Это - обычная строка. |
|
591
|
|
|
|
|
|
|
my $len=substr($st,0,my $p=index($st,$Div1)); # 0123.aaabbb |
|
592
|
|
|
|
|
|
|
$st=substr($st,$p+1); $$TotalLen+=$p+1+$len; # ^ ^p |
|
593
|
|
|
|
|
|
|
# Распаковываем исходную строку |
|
594
|
|
|
|
|
|
|
my $s=substr($st,0,$len); $PResult=\$s; |
|
595
|
|
|
|
|
|
|
} elsif($Type eq "L" || $Type eq "H") { |
|
596
|
|
|
|
|
|
|
my @Unpack; |
|
597
|
|
|
|
|
|
|
my $size=substr($st,0,my $p=index($st,$Div2)); |
|
598
|
|
|
|
|
|
|
$st=substr($st,$p+1); $$TotalLen+=$p+1; |
|
599
|
|
|
|
|
|
|
foreach my $i (0..$size-1) { |
|
600
|
|
|
|
|
|
|
my $len; push(@Unpack,_Unserialize($st,\$len)); |
|
601
|
|
|
|
|
|
|
$$TotalLen+=$len; |
|
602
|
|
|
|
|
|
|
$st=substr($st,$len); |
|
603
|
|
|
|
|
|
|
} |
|
604
|
|
|
|
|
|
|
if($Type eq "L") { $PResult=\@Unpack; } else { my %Hash=@Unpack; $PResult=\%Hash; } |
|
605
|
|
|
|
|
|
|
} |
|
606
|
|
|
|
|
|
|
# We have the pointer to the object $PResult. Returning the (n-1)-th reference on it. |
|
607
|
|
|
|
|
|
|
for(my $i=0; $i<$RefCount; $i++) { my $tmp=$PResult; $PResult=\$tmp; } |
|
608
|
|
|
|
|
|
|
if(ref($PResult) eq "ARRAY") { return wantarray?@$PResult:@$PResult[0]; } |
|
609
|
|
|
|
|
|
|
elsif(ref($PResult) eq "HASH") { return %$PResult; } |
|
610
|
|
|
|
|
|
|
else { return $$PResult; } |
|
611
|
|
|
|
|
|
|
} |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
# mixed _Unserialize(string $st) |
|
615
|
|
|
|
|
|
|
# Распаковывает строку, созданную ранее при помощи Serialize(). Возвращает то, что |
|
616
|
|
|
|
|
|
|
# было когда-то передано в параметрах Serialize. |
|
617
|
|
|
|
|
|
|
# В случае ошибки возвращает undef и выдает warning. |
|
618
|
|
|
|
|
|
|
sub Unserialize |
|
619
|
|
|
|
|
|
|
{ return undef if !defined $_[0]; |
|
620
|
|
|
|
|
|
|
my @Result=(); my $err=0; |
|
621
|
|
|
|
|
|
|
local $SIG{__WARN__}=sub { $err=1; }; |
|
622
|
|
|
|
|
|
|
local $SIG{__DIE__}=sub { $err=1; }; |
|
623
|
|
|
|
|
|
|
eval { @Result=_Unserialize($_[0]); }; |
|
624
|
|
|
|
|
|
|
if($err||$@) { return undef; } |
|
625
|
|
|
|
|
|
|
return wantarray?@Result:$Result[0]; |
|
626
|
|
|
|
|
|
|
} |
|
627
|
|
|
|
|
|
|
END_OF_FUNC |
|
628
|
|
|
|
|
|
|
); |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
#eval join("", values %CODE); |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
our $AUTOLOAD; |
|
633
|
|
|
|
|
|
|
sub AUTOLOAD { |
|
634
|
0
|
0
|
|
0
|
|
|
my ($pkg, $sub) = $AUTOLOAD =~ /^(.*)::(.*)$/s or return; |
|
635
|
0
|
0
|
|
|
|
|
return if $pkg ne __PACKAGE__; |
|
636
|
0
|
|
0
|
|
|
|
eval($CODE{$sub} or return); |
|
637
|
0
|
|
|
|
|
|
goto &$sub; |
|
638
|
|
|
|
|
|
|
} |
|
639
|
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
_reparseAll(); |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
return 1; |
|
643
|
|
|
|
|
|
|
__END__ |