line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package FTN::Outbound::BSO; |
2
|
|
|
|
|
|
|
$FTN::Outbound::BSO::VERSION = '20180823'; |
3
|
|
|
|
|
|
|
|
4
|
3
|
|
|
3
|
|
243197
|
use strict; |
|
3
|
|
|
|
|
20
|
|
|
3
|
|
|
|
|
75
|
|
5
|
3
|
|
|
3
|
|
11
|
use warnings; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
61
|
|
6
|
3
|
|
|
3
|
|
1913
|
use utf8; |
|
3
|
|
|
|
|
35
|
|
|
3
|
|
|
|
|
12
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# fts-5005.003 BinkleyTerm Style Outbound |
9
|
|
|
|
|
|
|
# except s/Continuous/Crash/g |
10
|
|
|
|
|
|
|
|
11
|
3
|
|
|
3
|
|
2272
|
use Log::Log4perl (); |
|
3
|
|
|
|
|
127360
|
|
|
3
|
|
|
|
|
68
|
|
12
|
3
|
|
|
3
|
|
27
|
use Scalar::Util (); |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
57
|
|
13
|
3
|
|
|
3
|
|
1401
|
use Encode::Locale (); |
|
3
|
|
|
|
|
37499
|
|
|
3
|
|
|
|
|
59
|
|
14
|
3
|
|
|
3
|
|
15
|
use Encode (); |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
41
|
|
15
|
3
|
|
|
3
|
|
11
|
use File::Spec (); |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
43
|
|
16
|
3
|
|
|
3
|
|
11
|
use Fcntl (); |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
29
|
|
17
|
3
|
|
|
3
|
|
1370
|
use FTN::Addr (); |
|
3
|
|
|
|
|
12044
|
|
|
3
|
|
|
|
|
54
|
|
18
|
3
|
|
|
3
|
|
1475
|
use FTN::Outbound::Reference_file (); |
|
3
|
|
|
|
|
5025
|
|
|
3
|
|
|
|
|
12580
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
my %flavour_extension = ( immediate => [ qw/ iut ilo / ], # Xut (netmail) Xlo (reference file) by fts-5005.003 |
21
|
|
|
|
|
|
|
# continuous => [ qw/ c c / ], # except this one |
22
|
|
|
|
|
|
|
crash => [ qw/ cut clo / ], |
23
|
|
|
|
|
|
|
direct => [ qw/ dut dlo / ], |
24
|
|
|
|
|
|
|
normal => [ qw/ out flo / ], |
25
|
|
|
|
|
|
|
hold => [ qw/ hut hlo / ], |
26
|
|
|
|
|
|
|
); |
27
|
|
|
|
|
|
|
# Reference files consist of a number of lines (terminated by 0x0a or 0x0d,0x0a) each consisting of the name of the file to transfer to the remote system. |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# file_type => extension. both keys and values should be unique in their sets |
30
|
|
|
|
|
|
|
# content notes are from fts-5005.003 |
31
|
|
|
|
|
|
|
my %control_file_extension = ( file_request => 'req', # file requests |
32
|
|
|
|
|
|
|
# The format of request files is documented in FTS-0006. |
33
|
|
|
|
|
|
|
busy => 'bsy', # busy control file. |
34
|
|
|
|
|
|
|
# may contain one line of PID information (less than 70 characters). |
35
|
|
|
|
|
|
|
call => 'csy', # call control file |
36
|
|
|
|
|
|
|
# may contain one line of PID information (less than 70 characters). |
37
|
|
|
|
|
|
|
hold => 'hld', # hold control file |
38
|
|
|
|
|
|
|
# must contain a one line string with the expiration of the hold period expressed in UNIX-time. |
39
|
|
|
|
|
|
|
try => 'try', # try control file |
40
|
|
|
|
|
|
|
# A try file (if implemented by a mailer) must contain the following: |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# NOK - Number of good connects, expressed as a 16-bit, big-endian integer. |
43
|
|
|
|
|
|
|
# NBAD - Number of bad connects, expressed as a 16-bit, big-endian integer. |
44
|
|
|
|
|
|
|
# CLength - Length of comment in bytes, expressed as an 8-bit unsigned integer. |
45
|
|
|
|
|
|
|
# Comment - CLength bytes, detailing the results of the most recent connection attempt. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# On completion of a successful session, NOK is incremented and NBAD is reset to zero. |
48
|
|
|
|
|
|
|
# On completion of a failed session, NBAD is incremented. |
49
|
|
|
|
|
|
|
# IF NBAD reaches the mailer's configured limit for failed sessions, |
50
|
|
|
|
|
|
|
# the node is marked undialable, NOK and NBAD are reset to zero, |
51
|
|
|
|
|
|
|
# and a hld control file is created in accordance with section 5.3. |
52
|
|
|
|
|
|
|
); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=head1 NAME |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
FTN::Outbound::BSO - working with BinkleyTerm Style Outbound. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 VERSION |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
version 20180823 |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=head1 SYNOPSIS |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
use Log::Log4perl (); |
65
|
|
|
|
|
|
|
use Encode (); |
66
|
|
|
|
|
|
|
use FTN::Outbound::BSO (); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Log::Log4perl -> easy_init( $Log::Log4perl::INFO ); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
my $bso = FTN::Outbound::BSO |
71
|
|
|
|
|
|
|
-> new( outbound_root => '/var/lib/ftn/outbound', |
72
|
|
|
|
|
|
|
domain => 'fidonet', |
73
|
|
|
|
|
|
|
zone => 2, |
74
|
|
|
|
|
|
|
domain_abbrev => { fidonet => '_out', |
75
|
|
|
|
|
|
|
homenet => 'leftnet', |
76
|
|
|
|
|
|
|
}, |
77
|
|
|
|
|
|
|
maximum_session_time => 3600, # one hour |
78
|
|
|
|
|
|
|
) or die 'cannot create bso object'; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
my $addr = FTN::Addr -> new( '2:451/30' ); |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub poll { |
83
|
|
|
|
|
|
|
my $addr = shift; |
84
|
|
|
|
|
|
|
my $bso = shift; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
my $flo = $bso -> addr_file_to_change( $addr, |
87
|
|
|
|
|
|
|
'reference_file', |
88
|
|
|
|
|
|
|
'normal' |
89
|
|
|
|
|
|
|
); |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
open my $fh, '>>', $flo |
92
|
|
|
|
|
|
|
or die sprintf 'cannot open %s: %s', $flo, $!; |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
print $fh ''; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
close $fh; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
$bso -> busy_protected_sub( $addr, |
100
|
|
|
|
|
|
|
\ &poll, |
101
|
|
|
|
|
|
|
); |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head1 DESCRIPTION |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
FTN::Outbound::BSO module is for working with BinkleyTerm Style Outbound in FTN following specifications from fts-5005.003 document. Figuring out correct file to process might be a tricky process: different casing, few our main domains, other differences. This module helps with this task. |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=head1 OBJECT CREATION |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head2 new |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Expects parameters as hash: |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
outbound_root - directory path as a character string where the whole outbound is located. Mandatory parameter. This directory should exist. |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
By standard constructor needs our domain and zone. They can be provided as: |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
our_addr - either FTN::Addr object representing our address or our address as a string which will be passed to FTN::Addr constructor. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
or as a pair: |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
domain - domain part of our address as described in frl-1028.002. |
122
|
|
|
|
|
|
|
zone - our zone in that domain |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
At least one of the ways should be provided. In case both are our_addr has higher priority. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
domain_abbrev - hash reference where keys are known domains and values are directory names (without extension) in outbound_root for those domains. Mandatory parameter. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
reference_file_read_line_transform_sub - reference to a function that receives an octet string and returns a character string. Will be passed to FTN::Outbound::Reference_file constructor. If not provided reference file content won't be processed. |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
maximum_session_time - maximum session time in seconds. If provided, all found busy files older than 2 * value will be removed during outbound scan. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
Returns newly created object on success. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=cut |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub new { |
137
|
2
|
|
|
2
|
1
|
4697
|
my $logger = Log::Log4perl -> get_logger( __PACKAGE__ ); |
138
|
|
|
|
|
|
|
|
139
|
2
|
50
|
|
|
|
691
|
ref( my $class = shift ) and $logger -> logcroak( "I'm only a class method!" ); |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
return |
142
|
2
|
50
|
|
|
|
7
|
unless @_; |
143
|
|
|
|
|
|
|
|
144
|
2
|
50
|
|
|
|
4
|
$logger -> logdie( sprintf 'constructor expects even number of arguments, but received %d of them', |
145
|
|
|
|
|
|
|
scalar @_, |
146
|
|
|
|
|
|
|
) |
147
|
|
|
|
|
|
|
if @_ % 2; |
148
|
|
|
|
|
|
|
|
149
|
2
|
|
|
|
|
10
|
my %option = @_; |
150
|
2
|
|
|
|
|
4
|
my %self; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# validating |
153
|
|
|
|
|
|
|
# mandatory parameters |
154
|
|
|
|
|
|
|
$logger -> logdie( 'mandatory outbound_root is not provided' ) |
155
|
2
|
50
|
|
|
|
6
|
unless exists $option{outbound_root}; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# outbound_root |
158
|
2
|
|
|
|
|
9
|
my $outbound_root_fs = Encode::encode( locale_fs => $option{outbound_root} ); |
159
|
|
|
|
|
|
|
|
160
|
2
|
50
|
|
|
|
274
|
unless ( -e $outbound_root_fs ) { |
161
|
|
|
|
|
|
|
$logger -> warn( sprintf 'outbound_root (%s) directory does not exist', |
162
|
|
|
|
|
|
|
$option{outbound_root}, |
163
|
0
|
|
|
|
|
0
|
); |
164
|
|
|
|
|
|
|
|
165
|
0
|
|
|
|
|
0
|
return; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
2
|
50
|
|
|
|
24
|
unless ( -d _ ) { # if it exists it should be a directory |
169
|
|
|
|
|
|
|
$logger -> warn( sprintf 'outbound_root (%s) does not point to the directory', |
170
|
|
|
|
|
|
|
$option{outbound_root}, |
171
|
0
|
|
|
|
|
0
|
); |
172
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
0
|
return; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
2
|
|
|
|
|
7
|
$self{outbound_root} = $option{outbound_root}; |
177
|
2
|
|
|
|
|
3
|
$self{outbound_root_fs} = $outbound_root_fs; |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
# our_addr or ( domain + zone ) |
181
|
2
|
50
|
33
|
|
|
15
|
if ( exists $option{our_addr} |
182
|
|
|
|
|
|
|
&& $option{our_addr} |
183
|
|
|
|
|
|
|
) { |
184
|
0
|
0
|
0
|
|
|
0
|
if ( ref $option{our_addr} |
|
|
|
0
|
|
|
|
|
185
|
|
|
|
|
|
|
&& Scalar::Util::blessed $option{our_addr} |
186
|
|
|
|
|
|
|
&& $option{our_addr} -> isa( 'FTN::Addr' ) |
187
|
|
|
|
|
|
|
) { |
188
|
0
|
|
|
|
|
0
|
$self{our_addr} = $option{our_addr}; |
189
|
|
|
|
|
|
|
} else { |
190
|
|
|
|
|
|
|
$self{our_addr} = FTN::Addr -> new( $option{our_addr} ) |
191
|
|
|
|
|
|
|
or $logger -> logdie( sprintf 'incorrect value of our_addr: %s', |
192
|
|
|
|
|
|
|
$option{our_addr}, |
193
|
0
|
0
|
|
|
|
0
|
); |
194
|
|
|
|
|
|
|
} |
195
|
0
|
|
|
|
|
0
|
$self{domain} = $self{our_addr} -> domain; |
196
|
0
|
|
|
|
|
0
|
$self{zone} = $self{our_addr} -> zone; |
197
|
|
|
|
|
|
|
} else { |
198
|
|
|
|
|
|
|
$logger -> logdie( 'domain is not provided' ) |
199
|
|
|
|
|
|
|
unless exists $option{domain} |
200
|
2
|
50
|
33
|
|
|
13
|
&& $option{domain}; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
$logger -> logdie( sprintf 'domain (%s) is not valid', |
203
|
|
|
|
|
|
|
$option{domain}, |
204
|
|
|
|
|
|
|
) |
205
|
2
|
50
|
|
|
|
13
|
unless $option{domain} =~ m/^[a-z\d_~-]{1,8}$/; # frl-1028.002 |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
$logger -> logdie( 'zone is not provided' ) |
208
|
|
|
|
|
|
|
unless exists $option{zone} |
209
|
2
|
50
|
33
|
|
|
22
|
&& $option{zone}; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
$logger -> logdie( sprintf 'zone (%s) is not valid', |
212
|
|
|
|
|
|
|
$option{zone}, |
213
|
|
|
|
|
|
|
) |
214
|
|
|
|
|
|
|
unless $option{zone} =~ m/^\d+$/ # FRL-1002.001, frl-1028.002 |
215
|
|
|
|
|
|
|
&& 1 <= $option{zone} # FRL-1002.001, frl-1028.002 |
216
|
2
|
50
|
33
|
|
|
19
|
&& $option{zone} <= 32767; # FRL-1002.001, frl-1028.002 |
|
|
|
33
|
|
|
|
|
217
|
|
|
|
|
|
|
|
218
|
2
|
|
|
|
|
5
|
$self{domain} = $option{domain}; |
219
|
2
|
|
|
|
|
4
|
$self{zone} = $option{zone}; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
# domain abbreviations |
223
|
2
|
50
|
33
|
|
|
14
|
if ( exists $option{domain_abbrev} |
|
|
|
33
|
|
|
|
|
224
|
|
|
|
|
|
|
&& defined $option{domain_abbrev} |
225
|
|
|
|
|
|
|
&& ref $option{domain_abbrev} eq 'HASH' |
226
|
|
|
|
|
|
|
) { |
227
|
|
|
|
|
|
|
$logger -> logdie( sprintf 'our domain (%s) is not in the passed domain_abbrev hash!', |
228
|
|
|
|
|
|
|
$self{domain}, |
229
|
|
|
|
|
|
|
) |
230
|
2
|
50
|
|
|
|
8
|
unless exists $option{domain_abbrev}{ $self{domain} }; |
231
|
|
|
|
|
|
|
|
232
|
2
|
|
|
|
|
3
|
$self{domain_abbrev} = $option{domain_abbrev}; |
233
|
|
|
|
|
|
|
} else { |
234
|
0
|
|
|
|
|
0
|
$logger -> logdie( 'no valid domain_abbrev provided' ); |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# reference file read line transform sub |
238
|
2
|
50
|
|
|
|
11
|
if ( exists $option{reference_file_read_line_transform_sub} ) { |
239
|
|
|
|
|
|
|
$logger -> logdie( 'incorrect value of reference_file_read_line_transform_sub (should be a sub reference)' ) |
240
|
|
|
|
|
|
|
unless defined $option{reference_file_read_line_transform_sub} |
241
|
0
|
0
|
0
|
|
|
0
|
&& 'CODE' eq ref $option{reference_file_read_line_transform_sub}; |
242
|
|
|
|
|
|
|
|
243
|
0
|
|
|
|
|
0
|
$self{reference_file_read_line_transform_sub} = $option{reference_file_read_line_transform_sub}; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# maximum_session_time |
247
|
2
|
50
|
|
|
|
6
|
if ( exists $option{maximum_session_time} ) { |
248
|
|
|
|
|
|
|
$logger -> logdie( sprintf 'incorrect value of maximum_session_time: %s', |
249
|
|
|
|
|
|
|
defined $option{maximum_session_time} ? |
250
|
|
|
|
|
|
|
$option{maximum_session_time} |
251
|
|
|
|
|
|
|
: 'undef' |
252
|
|
|
|
|
|
|
) |
253
|
|
|
|
|
|
|
unless defined $option{maximum_session_time} |
254
|
|
|
|
|
|
|
&& $option{maximum_session_time} =~ m/^\d+$/ |
255
|
0
|
0
|
0
|
|
|
0
|
&& $option{maximum_session_time}; # cannot be 0 |
|
|
0
|
0
|
|
|
|
|
256
|
|
|
|
|
|
|
|
257
|
0
|
|
|
|
|
0
|
$self{maximum_session_time} = $option{maximum_session_time}; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
2
|
|
|
|
|
10
|
bless \ %self, $class; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub _store { |
264
|
0
|
|
|
0
|
|
0
|
my $logger = Log::Log4perl -> get_logger( __PACKAGE__ ); |
265
|
|
|
|
|
|
|
|
266
|
0
|
0
|
|
|
|
0
|
ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" ); |
267
|
|
|
|
|
|
|
|
268
|
0
|
|
|
|
|
0
|
my ( $file_prop, |
269
|
|
|
|
|
|
|
$ext, |
270
|
|
|
|
|
|
|
$target, |
271
|
|
|
|
|
|
|
$net, |
272
|
|
|
|
|
|
|
$node, |
273
|
|
|
|
|
|
|
$point, |
274
|
|
|
|
|
|
|
) = @_; |
275
|
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
0
|
my %ext_netmail_flavour = map { $flavour_extension{ $_ }[ 0 ] => $_ } keys %flavour_extension; |
|
0
|
|
|
|
|
0
|
|
277
|
0
|
|
|
|
|
0
|
my %ext_reference_file_flavour = map { $flavour_extension{ $_ }[ 1 ] => $_ } keys %flavour_extension; |
|
0
|
|
|
|
|
0
|
|
278
|
0
|
|
|
|
|
0
|
my %ext_control_file = reverse %control_file_extension; |
279
|
|
|
|
|
|
|
|
280
|
0
|
|
|
|
|
0
|
my $lc_ext = lc $ext; |
281
|
|
|
|
|
|
|
|
282
|
0
|
0
|
|
|
|
0
|
if ( exists $ext_netmail_flavour{ $lc_ext } ) { # netmail |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
283
|
0
|
|
|
|
|
0
|
push @{ $target -> { $net }{ $node }{ $point }{netmail}{ $ext_netmail_flavour{ $lc_ext } } }, |
|
0
|
|
|
|
|
0
|
|
284
|
|
|
|
|
|
|
$file_prop; |
285
|
|
|
|
|
|
|
} elsif ( exists $ext_reference_file_flavour{ $lc_ext } ) { # reference file |
286
|
0
|
|
|
|
|
0
|
my $flavour = $ext_reference_file_flavour{ $lc_ext }; |
287
|
|
|
|
|
|
|
# referenced files |
288
|
0
|
0
|
0
|
|
|
0
|
if ( $file_prop -> {size} # empty files are empty, right? |
289
|
|
|
|
|
|
|
&& exists $self -> {reference_file_read_line_transform_sub} |
290
|
|
|
|
|
|
|
) { |
291
|
|
|
|
|
|
|
$file_prop -> {referenced_files} = |
292
|
|
|
|
|
|
|
FTN::Outbound::Reference_file |
293
|
|
|
|
|
|
|
-> new( $file_prop -> {full_name}, |
294
|
|
|
|
|
|
|
$self -> {reference_file_read_line_transform_sub}, |
295
|
|
|
|
|
|
|
) |
296
|
0
|
|
|
|
|
0
|
-> read_existing_file |
297
|
|
|
|
|
|
|
-> referenced_files; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
0
|
|
|
|
|
0
|
push @{ $target -> { $net }{ $node }{ $point }{reference_file}{ $flavour } }, |
|
0
|
|
|
|
|
0
|
|
301
|
|
|
|
|
|
|
$file_prop; |
302
|
|
|
|
|
|
|
} elsif ( exists $ext_control_file{ $lc_ext } ) { |
303
|
0
|
0
|
|
|
|
0
|
my $age = $file_prop -> {mstat} ? time - $file_prop -> {mstat} : 0; |
304
|
0
|
0
|
0
|
|
|
0
|
if ( $ext_control_file{ $lc_ext } eq 'busy' |
|
|
|
0
|
|
|
|
|
305
|
|
|
|
|
|
|
&& exists $self -> {maximum_session_time} |
306
|
|
|
|
|
|
|
&& $self -> {maximum_session_time} * 2 < $age |
307
|
|
|
|
|
|
|
) { # try to remove if maximum_session_time is defined and busy is older than it |
308
|
|
|
|
|
|
|
$logger -> info( sprintf 'removing expired busy %s (%d seconds old)', |
309
|
|
|
|
|
|
|
$file_prop -> {full_name}, |
310
|
0
|
|
|
|
|
0
|
$age, |
311
|
|
|
|
|
|
|
); |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
unlink Encode::encode( locale_fs => $file_prop -> {full_name} ) |
314
|
|
|
|
|
|
|
or $logger -> logdie( sprintf 'could not unlink %s: %s', |
315
|
|
|
|
|
|
|
$file_prop -> {full_name}, |
316
|
0
|
0
|
|
|
|
0
|
$!, |
317
|
|
|
|
|
|
|
); |
318
|
|
|
|
|
|
|
} else { |
319
|
0
|
|
|
|
|
0
|
push @{ $target -> { $net }{ $node }{ $point }{ $ext_control_file{ $lc_ext } } }, |
|
0
|
|
|
|
|
0
|
|
320
|
|
|
|
|
|
|
$file_prop; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=head1 OBJECT METHODS |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=head2 scan |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
Scans outbound for all known domains. Old busy files might be removed. |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Returns itself for chaining. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=cut |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
sub scan { |
336
|
0
|
|
|
0
|
1
|
0
|
my $logger = Log::Log4perl -> get_logger( __PACKAGE__ ); |
337
|
|
|
|
|
|
|
|
338
|
0
|
0
|
|
|
|
0
|
ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" ); |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
$logger -> logdie( sprintf 'outbound_root (%s) directory does not exist', |
341
|
|
|
|
|
|
|
$self -> {outbound_root}, |
342
|
|
|
|
|
|
|
) |
343
|
0
|
0
|
|
|
|
0
|
unless -e $self -> {outbound_root_fs}; |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# if it exists it should be a directory |
346
|
|
|
|
|
|
|
$logger -> logdie( sprintf 'outbound_root (%s) does not point to the directory', |
347
|
|
|
|
|
|
|
$self -> {outbound_root}, |
348
|
|
|
|
|
|
|
) |
349
|
0
|
0
|
|
|
|
0
|
unless -d _; |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# check outbound_root for all domain abbrevs and zones |
352
|
0
|
|
|
|
|
0
|
my $domain_abbr_re = join '|', values %{ $self -> {domain_abbrev} }; |
|
0
|
|
|
|
|
0
|
|
353
|
0
|
|
|
|
|
0
|
my %result; |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
opendir my $or_dh, $self -> {outbound_root_fs} |
356
|
|
|
|
|
|
|
or $logger -> logdie( sprintf 'cannot opendir %s: %s', |
357
|
|
|
|
|
|
|
$self -> {outbound_root}, |
358
|
0
|
0
|
|
|
|
0
|
$!, |
359
|
|
|
|
|
|
|
); |
360
|
|
|
|
|
|
|
|
361
|
0
|
|
|
|
|
0
|
while ( my $dz_out = readdir $or_dh ) { # looking for domain abbreviations directories |
362
|
0
|
|
|
|
|
0
|
$dz_out = Encode::decode( locale_fs => $dz_out ); |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
next # skipping hidden files and .. |
365
|
0
|
0
|
|
|
|
0
|
if '.' eq substr $dz_out, 0, 1; |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
my $dir_name = File::Spec -> catdir( $self -> {outbound_root}, |
368
|
0
|
|
|
|
|
0
|
$dz_out, |
369
|
|
|
|
|
|
|
); |
370
|
|
|
|
|
|
|
|
371
|
0
|
|
|
|
|
0
|
my $dir_name_fs = Encode::encode( locale_fs => $dir_name ); |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
next # looking only for directories |
374
|
0
|
0
|
|
|
|
0
|
unless -d $dir_name_fs; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# our_domain_dir, our_domain_dir.7fff, other_domain.1999 |
377
|
|
|
|
|
|
|
next |
378
|
|
|
|
|
|
|
unless $dz_out =~ /^($domain_abbr_re)(?:\.([1-7]?[0-9a-f]{3}))?$/i |
379
|
|
|
|
|
|
|
&& ( $1 eq $self -> {domain_abbrev}{ $self -> {domain} } |
380
|
0
|
0
|
0
|
|
|
0
|
|| defined $2 |
|
|
|
0
|
|
|
|
|
381
|
|
|
|
|
|
|
); |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
my ( $domain ) = grep $self -> {domain_abbrev}{ $_ } eq $1, |
384
|
0
|
|
|
|
|
0
|
keys %{ $self -> {domain_abbrev} }; |
|
0
|
|
|
|
|
0
|
|
385
|
|
|
|
|
|
|
|
386
|
0
|
0
|
|
|
|
0
|
my $zone = defined $2 ? hex $2 : $self -> {zone}; |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
next |
389
|
0
|
0
|
0
|
|
|
0
|
unless 1 <= $zone && $zone <= 32767; # FRL-1002.001, frl-1028.002 |
390
|
|
|
|
|
|
|
|
391
|
0
|
|
|
|
|
0
|
$logger -> debug( sprintf 'directory %s matches. domain: %s zone: %s', |
392
|
|
|
|
|
|
|
$dz_out, |
393
|
|
|
|
|
|
|
$domain, |
394
|
|
|
|
|
|
|
$zone, |
395
|
|
|
|
|
|
|
); |
396
|
|
|
|
|
|
|
|
397
|
0
|
|
|
|
|
0
|
$result{ $domain }{ $zone }{ $dz_out }{dir} = $dir_name; |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# now let's traverse found domain_abbrev[.zone] dir |
400
|
0
|
0
|
|
|
|
0
|
opendir my $dz_dh, $dir_name_fs |
401
|
|
|
|
|
|
|
or $logger -> logdie( sprintf 'cannot opendir %s: %s', |
402
|
|
|
|
|
|
|
$dir_name, |
403
|
|
|
|
|
|
|
$!, |
404
|
|
|
|
|
|
|
); |
405
|
|
|
|
|
|
|
|
406
|
0
|
|
|
|
|
0
|
while ( my $dir_entry = readdir $dz_dh ) { |
407
|
0
|
|
|
|
|
0
|
$dir_entry = Encode::decode( locale_fs => $dir_entry ); |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
next |
410
|
0
|
0
|
|
|
|
0
|
unless $dir_entry =~ m/^([0-9a-f]{4})([0-9a-f]{4})\.(.+)$/i; |
411
|
|
|
|
|
|
|
|
412
|
0
|
|
|
|
|
0
|
my ( $net, $node ) = map hex, $1, $2; |
413
|
0
|
|
|
|
|
0
|
my $ext = $3; |
414
|
|
|
|
|
|
|
|
415
|
0
|
|
|
|
|
0
|
my $full_name = File::Spec -> catfile( $dir_name, |
416
|
|
|
|
|
|
|
$dir_entry, |
417
|
|
|
|
|
|
|
); |
418
|
|
|
|
|
|
|
|
419
|
0
|
|
|
|
|
0
|
my $full_name_fs = Encode::encode( locale_fs => $full_name ); |
420
|
|
|
|
|
|
|
|
421
|
0
|
0
|
0
|
|
|
0
|
if ( lc( $ext ) eq 'pnt' |
|
|
0
|
|
|
|
|
|
422
|
|
|
|
|
|
|
&& -d $full_name_fs |
423
|
|
|
|
|
|
|
) { # points subdir |
424
|
0
|
|
|
|
|
0
|
$logger -> debug( sprintf 'found %s#%d:%d/%d points subdirectory %s', |
425
|
|
|
|
|
|
|
$domain, |
426
|
|
|
|
|
|
|
$zone, |
427
|
|
|
|
|
|
|
$net, |
428
|
|
|
|
|
|
|
$node, |
429
|
|
|
|
|
|
|
$full_name, |
430
|
|
|
|
|
|
|
); |
431
|
|
|
|
|
|
|
|
432
|
0
|
|
|
|
|
0
|
$result{ $domain }{ $zone }{ $dz_out }{ $net }{ $node }{points_dir}{ $dir_entry } = $full_name; # might be different hex casing for net/node or extension |
433
|
|
|
|
|
|
|
|
434
|
0
|
0
|
|
|
|
0
|
opendir my $p_dh, $full_name_fs |
435
|
|
|
|
|
|
|
or $logger -> logdie( sprintf 'cannot opendir %s: %s', |
436
|
|
|
|
|
|
|
$full_name, |
437
|
|
|
|
|
|
|
$!, |
438
|
|
|
|
|
|
|
); |
439
|
|
|
|
|
|
|
|
440
|
0
|
|
|
|
|
0
|
while ( my $file = readdir $p_dh ) { |
441
|
0
|
|
|
|
|
0
|
$file = Encode::decode( locale_fs => $file ); |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
next |
444
|
0
|
0
|
|
|
|
0
|
unless $file =~ m/^([0-9a-f]{8})\.(.+)$/i; |
445
|
|
|
|
|
|
|
|
446
|
0
|
|
|
|
|
0
|
my $point = hex $1; |
447
|
0
|
|
|
|
|
0
|
my $ext = $2; |
448
|
|
|
|
|
|
|
|
449
|
0
|
|
|
|
|
0
|
my $full_name = File::Spec -> catfile( $full_name, |
450
|
|
|
|
|
|
|
$file, |
451
|
|
|
|
|
|
|
); |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
next # in points dir we're interested in files only |
454
|
0
|
0
|
|
|
|
0
|
unless -f Encode::encode( locale_fs => $full_name ); |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
$self -> _store( { name => $file, |
457
|
|
|
|
|
|
|
full_name => $full_name, |
458
|
|
|
|
|
|
|
size => -s _, |
459
|
|
|
|
|
|
|
mstat => ( stat _ )[ 9 ], |
460
|
|
|
|
|
|
|
}, |
461
|
|
|
|
|
|
|
$ext, |
462
|
0
|
|
|
|
|
0
|
$result{ $domain }{ $zone }{ $dz_out }, |
463
|
|
|
|
|
|
|
$net, |
464
|
|
|
|
|
|
|
$node, |
465
|
|
|
|
|
|
|
$point, |
466
|
|
|
|
|
|
|
); |
467
|
|
|
|
|
|
|
} |
468
|
0
|
|
|
|
|
0
|
closedir $p_dh; |
469
|
|
|
|
|
|
|
} elsif ( -f $full_name_fs ) { # node related file |
470
|
|
|
|
|
|
|
$self -> _store( { name => $_, |
471
|
|
|
|
|
|
|
full_name => $full_name, |
472
|
|
|
|
|
|
|
size => -s _, |
473
|
|
|
|
|
|
|
mstat => ( stat _ )[ 9 ], |
474
|
|
|
|
|
|
|
}, |
475
|
|
|
|
|
|
|
$ext, |
476
|
0
|
|
|
|
|
0
|
$result{ $domain }{ $zone }{ $dz_out }, |
477
|
|
|
|
|
|
|
$net, |
478
|
|
|
|
|
|
|
$node, |
479
|
|
|
|
|
|
|
0, # point |
480
|
|
|
|
|
|
|
); |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
} |
483
|
0
|
|
|
|
|
0
|
closedir $dz_dh; |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
} |
486
|
0
|
|
|
|
|
0
|
closedir $or_dh; |
487
|
|
|
|
|
|
|
|
488
|
0
|
|
|
|
|
0
|
$self -> {scanned} = \ %result; |
489
|
|
|
|
|
|
|
|
490
|
0
|
|
|
|
|
0
|
$self; |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
=head2 scanned_hash |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
Returns internal structure representing scanned outbound (hash in list context, hashref in scalar context). If scan method hasn't been called, it will be called implicitly by this method. |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=cut |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
sub scanned_hash { |
500
|
0
|
|
|
0
|
1
|
0
|
my $logger = Log::Log4perl -> get_logger( __PACKAGE__ ); |
501
|
|
|
|
|
|
|
|
502
|
0
|
0
|
|
|
|
0
|
ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" ); |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
$self -> scan |
505
|
0
|
0
|
|
|
|
0
|
unless exists $self -> {scanned}; |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
wantarray ? |
508
|
0
|
|
|
|
|
0
|
%{ $self -> {scanned} } |
509
|
0
|
0
|
|
|
|
0
|
: $self -> {scanned}; |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
sub _validate_addr { |
514
|
0
|
|
|
0
|
|
0
|
my $logger = Log::Log4perl -> get_logger( __PACKAGE__ ); |
515
|
|
|
|
|
|
|
|
516
|
0
|
0
|
|
|
|
0
|
ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" ); |
517
|
|
|
|
|
|
|
|
518
|
0
|
|
|
|
|
0
|
my $addr = shift; |
519
|
|
|
|
|
|
|
|
520
|
0
|
0
|
0
|
|
|
0
|
$logger -> logdie( 'no valid address passed' ) |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
521
|
|
|
|
|
|
|
unless defined $addr |
522
|
|
|
|
|
|
|
&& ref $addr |
523
|
|
|
|
|
|
|
&& Scalar::Util::blessed $addr |
524
|
|
|
|
|
|
|
&& $addr -> isa( 'FTN::Addr' ); |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
$logger -> logdie( 'passed address has unknown domain: %s', |
527
|
|
|
|
|
|
|
$addr -> domain, |
528
|
|
|
|
|
|
|
) |
529
|
0
|
0
|
|
|
|
0
|
unless exists $self -> {domain_abbrev}{ $addr -> domain }; |
530
|
|
|
|
|
|
|
|
531
|
0
|
|
|
|
|
0
|
$addr; |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=head2 is_busy |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
Expects one parameter - address as FTN::Addr object. Returns true if that address is busy (connection session, mail processing, ...). |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=cut |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
sub is_busy { |
541
|
0
|
|
|
0
|
1
|
0
|
my $logger = Log::Log4perl -> get_logger( __PACKAGE__ ); |
542
|
|
|
|
|
|
|
|
543
|
0
|
0
|
|
|
|
0
|
ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" ); |
544
|
|
|
|
|
|
|
|
545
|
0
|
|
|
|
|
0
|
my $addr = $self -> _validate_addr( shift ); |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
$self -> scan |
548
|
0
|
0
|
|
|
|
0
|
unless exists $self -> {scanned}; |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
exists $self -> {scanned}{ $addr -> domain } |
551
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $addr -> domain }{ $addr -> zone } |
552
|
|
|
|
|
|
|
&& grep { exists $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $_ }{ $addr -> net } |
553
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $_ }{ $addr -> net }{ $addr -> node } |
554
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $_ }{ $addr -> net }{ $addr -> node }{ $addr -> point } |
555
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $_ }{ $addr -> net }{ $addr -> node }{ $addr -> point }{busy} |
556
|
0
|
0
|
0
|
|
|
0
|
} keys %{ $self -> {scanned}{ $addr -> domain }{ $addr -> zone } }; |
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
sub _select_domain_zone_dir { # best one. for updating. for checking needs a list (another method or direct access to the structure) |
560
|
|
|
|
|
|
|
# and makes one if it doesn't exist or isn't good enough (e.g. our_domain_abbr.our_zone) |
561
|
15
|
|
|
15
|
|
503
|
my $logger = Log::Log4perl -> get_logger( __PACKAGE__ ); |
562
|
|
|
|
|
|
|
|
563
|
15
|
50
|
|
|
|
319
|
ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" ); |
564
|
|
|
|
|
|
|
|
565
|
15
|
|
|
|
|
17
|
my $domain = shift; |
566
|
15
|
|
|
|
|
17
|
my $zone = shift; |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
$logger -> logdie( 'unknown domain: %s', |
569
|
|
|
|
|
|
|
$domain, |
570
|
|
|
|
|
|
|
) |
571
|
15
|
50
|
|
|
|
26
|
unless exists $self -> {domain_abbrev}{ $domain }; |
572
|
|
|
|
|
|
|
|
573
|
15
|
|
|
|
|
18
|
my $best_match = $self -> {domain_abbrev}{ $domain }; |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
$best_match .= sprintf( '.%03x', $zone ) |
576
|
|
|
|
|
|
|
unless $domain eq $self -> {domain} |
577
|
15
|
100
|
66
|
|
|
89
|
&& $zone == $self -> {zone}; |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
$self -> scan |
580
|
15
|
50
|
|
|
|
25
|
unless exists $self -> {scanned}; |
581
|
|
|
|
|
|
|
|
582
|
15
|
50
|
33
|
|
|
106
|
if ( exists $self -> {scanned}{ $domain } |
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
583
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $domain }{ $zone } |
584
|
|
|
|
|
|
|
&& ( $domain ne $self -> {domain} # other domains have zones in extensions |
585
|
|
|
|
|
|
|
|| $zone != $self -> {zone} # other zones in our domain have zones in extensions |
586
|
|
|
|
|
|
|
|| grep length( $_ ) == length( $best_match ), |
587
|
|
|
|
|
|
|
keys %{ $self -> {scanned}{ $domain }{ $zone } } |
588
|
|
|
|
|
|
|
) |
589
|
|
|
|
|
|
|
) { |
590
|
37
|
50
|
|
|
|
81
|
my @list = sort { length $a <=> length $b |
591
|
|
|
|
|
|
|
|| $b cmp $a # we prefer lower case |
592
|
|
|
|
|
|
|
} |
593
|
15
|
|
|
|
|
15
|
keys %{ $self -> {scanned}{ $domain }{ $zone } }; |
|
15
|
|
|
|
|
55
|
|
594
|
|
|
|
|
|
|
|
595
|
15
|
|
|
|
|
36
|
my ( $t ) = grep $_ eq $best_match, @list; # might be exact case |
596
|
|
|
|
|
|
|
|
597
|
15
|
100
|
|
|
|
34
|
$best_match = defined $t ? |
598
|
|
|
|
|
|
|
$t |
599
|
|
|
|
|
|
|
: $list[ 0 ]; # if didn't find the best match, use the very first existing one |
600
|
|
|
|
|
|
|
} else { # need to create |
601
|
|
|
|
|
|
|
my $dir_full_name = File::Spec -> catdir( $self -> {outbound_root}, |
602
|
0
|
|
|
|
|
0
|
$best_match, |
603
|
|
|
|
|
|
|
); |
604
|
|
|
|
|
|
|
|
605
|
0
|
|
|
|
|
0
|
$logger -> debug( sprintf 'creating directory for domain %s zone %d: %s', |
606
|
|
|
|
|
|
|
$domain, |
607
|
|
|
|
|
|
|
$zone, |
608
|
|
|
|
|
|
|
$dir_full_name, |
609
|
|
|
|
|
|
|
); |
610
|
|
|
|
|
|
|
|
611
|
0
|
0
|
|
|
|
0
|
mkdir Encode::encode( locale_fs => $dir_full_name ) |
612
|
|
|
|
|
|
|
or $logger -> logdie( sprintf 'cannot create domain/zone %s directory: %s', |
613
|
|
|
|
|
|
|
$dir_full_name, |
614
|
|
|
|
|
|
|
$!, |
615
|
|
|
|
|
|
|
); |
616
|
|
|
|
|
|
|
|
617
|
0
|
|
|
|
|
0
|
$self -> {scanned}{ $domain }{ $zone }{ $best_match }{dir} = $dir_full_name; |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
|
620
|
15
|
|
|
|
|
30
|
$best_match; |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
sub _select_points_dir { # select best existing. or make it. for updating |
624
|
9
|
|
|
9
|
|
4277
|
my $logger = Log::Log4perl -> get_logger( __PACKAGE__ ); |
625
|
|
|
|
|
|
|
|
626
|
9
|
50
|
|
|
|
266
|
ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" ); |
627
|
|
|
|
|
|
|
|
628
|
9
|
|
|
|
|
14
|
my ( $domain, |
629
|
|
|
|
|
|
|
$zone, |
630
|
|
|
|
|
|
|
$net, |
631
|
|
|
|
|
|
|
$node, |
632
|
|
|
|
|
|
|
) = @_; |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
$logger -> logdie( 'unknown domain: %s', |
635
|
|
|
|
|
|
|
$domain, |
636
|
|
|
|
|
|
|
) |
637
|
9
|
50
|
|
|
|
16
|
unless exists $self -> {domain_abbrev}{ $domain }; |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
# domain zone dir might not exist at all |
640
|
9
|
|
|
|
|
14
|
my $dz_out = $self -> _select_domain_zone_dir( $domain, $zone ); |
641
|
9
|
|
|
|
|
30
|
my $points_dir = sprintf( '%04x%04x.pnt', |
642
|
|
|
|
|
|
|
$net, |
643
|
|
|
|
|
|
|
$node, |
644
|
|
|
|
|
|
|
); |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
# what if domain_abbr.zone (perfect one) doesn't have required points dir |
647
|
|
|
|
|
|
|
# but domain_abbr.zOnE has? |
648
|
6
|
|
|
|
|
13
|
my @dz_out_with_existing_points_dir = sort { $b cmp $a } # we prefer lower case of domain[.zone] dir |
649
|
|
|
|
|
|
|
grep length $_ == length $dz_out # to filter out our_domain.our_zone versions |
650
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $domain }{ $zone }{ $_ }{ $net } |
651
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $domain }{ $zone }{ $_ }{ $net }{ $node } |
652
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $domain }{ $zone }{ $_ }{ $net }{ $node }{points_dir}, |
653
|
9
|
|
33
|
|
|
10
|
keys %{ $self -> {scanned}{ $domain }{ $zone } }; |
|
9
|
|
|
|
|
117
|
|
654
|
|
|
|
|
|
|
|
655
|
9
|
50
|
|
|
|
15
|
if ( @dz_out_with_existing_points_dir ) { # ok, there is at least one with points dir. how do we select best of them? |
656
|
|
|
|
|
|
|
# let's prioritize domain_abbr[.zone] betterness over points_dir betterness |
657
|
9
|
100
|
|
|
|
20
|
unless ( grep $_ eq $dz_out, |
658
|
|
|
|
|
|
|
@dz_out_with_existing_points_dir |
659
|
|
|
|
|
|
|
) { # ok, there is no best domain_abbr[.zone]. let's try to find best points_dir |
660
|
7
|
|
|
|
|
16
|
my ( $t ) = grep exists $self -> {scanned}{ $domain }{ $zone }{ $_ }{ $net }{ $node }{points_dir}{ $points_dir }, |
661
|
|
|
|
|
|
|
@dz_out_with_existing_points_dir; |
662
|
|
|
|
|
|
|
|
663
|
7
|
100
|
|
|
|
14
|
$dz_out = defined $t ? $t : $dz_out_with_existing_points_dir[ 0 ]; # if no best in either place, just use the very first one |
664
|
|
|
|
|
|
|
} |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
# now we've got best outbound. let's find best points dir. or just the very first |
667
|
6
|
|
|
|
|
8
|
$points_dir = ( sort { $b cmp $a } # we prefer lower case of points dir |
668
|
4
|
|
|
|
|
14
|
keys %{ $self -> {scanned}{ $domain }{ $zone }{ $dz_out }{ $net }{ $node }{points_dir} } |
669
|
|
|
|
|
|
|
)[ 0 ] |
670
|
9
|
100
|
|
|
|
18
|
unless exists $self -> {scanned}{ $domain }{ $zone }{ $dz_out }{ $net }{ $node }{points_dir}{ $points_dir }; |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
} else { # doesn't exist. we need to create it using best domain_abbr[.zone] dir |
673
|
|
|
|
|
|
|
my $dir_full_name = File::Spec -> catdir( $self -> {scanned}{ $domain }{ $zone }{ $dz_out }{dir}, |
674
|
0
|
|
|
|
|
0
|
$points_dir, |
675
|
|
|
|
|
|
|
); |
676
|
|
|
|
|
|
|
|
677
|
0
|
|
|
|
|
0
|
$logger -> debug( sprintf 'creating %s#%d:%d/%d points directory %s', |
678
|
|
|
|
|
|
|
$domain, |
679
|
|
|
|
|
|
|
$zone, |
680
|
|
|
|
|
|
|
$net, |
681
|
|
|
|
|
|
|
$node, |
682
|
|
|
|
|
|
|
$dir_full_name, |
683
|
|
|
|
|
|
|
); |
684
|
|
|
|
|
|
|
|
685
|
0
|
0
|
|
|
|
0
|
mkdir Encode::encode( locale_fs => $dir_full_name ) |
686
|
|
|
|
|
|
|
or $logger -> logdie( sprintf 'cannot create points directory %s: %s', |
687
|
|
|
|
|
|
|
$dir_full_name, |
688
|
|
|
|
|
|
|
$!, |
689
|
|
|
|
|
|
|
); |
690
|
|
|
|
|
|
|
|
691
|
0
|
|
|
|
|
0
|
$self -> {scanned}{ $domain }{ $zone }{ $dz_out }{ $net }{ $node }{points_dir}{ $points_dir } = $dir_full_name; |
692
|
|
|
|
|
|
|
} |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
# return ( dz_out, $points_dir) or full points directory path? |
695
|
9
|
|
|
|
|
36
|
$self -> {scanned}{ $domain }{ $zone }{ $dz_out }{ $net }{ $node }{points_dir}{ $points_dir }; |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
=head2 busy_protected_sub |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
Expects two parameters: |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
address going to be dealt with as a FTN::Addr object |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
function reference that will receive passed address and us ($self) as parameters and which should do all required operations related to the passed address. |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
This method infinitely waits (most likely will be changed in the future) until address is not busy. Then it creates busy flag and calls passed function reference providing itself as an argument for it. After function return removes created busy flag. |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
Returns itself for chaining. |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
=cut |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
sub busy_protected_sub { # address, sub_ref( self ). (order busy, execute sub, remove busy) |
713
|
0
|
|
|
0
|
1
|
|
my $logger = Log::Log4perl -> get_logger( __PACKAGE__ ); |
714
|
|
|
|
|
|
|
|
715
|
0
|
0
|
|
|
|
|
ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" ); |
716
|
|
|
|
|
|
|
|
717
|
0
|
|
|
|
|
|
my $addr = $self -> _validate_addr( shift ); |
718
|
|
|
|
|
|
|
|
719
|
0
|
0
|
0
|
|
|
|
$logger -> logdie( 'no valid sub_ref passed' ) |
|
|
|
0
|
|
|
|
|
720
|
|
|
|
|
|
|
unless @_ |
721
|
|
|
|
|
|
|
&& defined $_[ 0 ] |
722
|
|
|
|
|
|
|
&& 'CODE' eq ref $_[ 0 ]; |
723
|
|
|
|
|
|
|
|
724
|
0
|
|
|
|
|
|
my $sub_ref = shift; |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
$self -> scan |
727
|
0
|
0
|
|
|
|
|
unless exists $self -> {scanned}; |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
# check that it's not already busy |
730
|
0
|
|
|
|
|
|
while ( $self -> is_busy( $addr ) ) { |
731
|
0
|
|
|
|
|
|
sleep( 4 ); # waiting... |
732
|
0
|
|
|
|
|
|
$self -> scan; |
733
|
|
|
|
|
|
|
} |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
# here there is no busy flag for passed address. make it in the best dir then |
736
|
0
|
|
|
|
|
|
my $busy_name; |
737
|
|
|
|
|
|
|
|
738
|
0
|
0
|
|
|
|
|
if ( $addr -> point ) { # possible dir creation |
739
|
0
|
|
|
|
|
|
$busy_name = File::Spec -> catfile( $self -> _select_points_dir( $addr -> domain, |
740
|
|
|
|
|
|
|
$addr -> zone, |
741
|
|
|
|
|
|
|
$addr -> net, |
742
|
|
|
|
|
|
|
$addr -> node, |
743
|
|
|
|
|
|
|
), |
744
|
|
|
|
|
|
|
sprintf( '%08x', |
745
|
|
|
|
|
|
|
$addr -> point, |
746
|
|
|
|
|
|
|
), |
747
|
|
|
|
|
|
|
); |
748
|
|
|
|
|
|
|
} else { |
749
|
0
|
|
|
|
|
|
my $dz_out = $self -> _select_domain_zone_dir( $addr -> domain, |
750
|
|
|
|
|
|
|
$addr -> zone, |
751
|
|
|
|
|
|
|
); |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
$busy_name = File::Spec -> catfile( $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $dz_out }{dir}, |
754
|
0
|
|
|
|
|
|
sprintf( '%04x%04x', |
755
|
|
|
|
|
|
|
$addr -> net, |
756
|
|
|
|
|
|
|
$addr -> node, |
757
|
|
|
|
|
|
|
), |
758
|
|
|
|
|
|
|
); |
759
|
|
|
|
|
|
|
} |
760
|
0
|
|
|
|
|
|
$busy_name .= '.' . $control_file_extension{busy}; |
761
|
|
|
|
|
|
|
|
762
|
0
|
|
|
|
|
|
my $busy_name_fs = Encode::encode( locale_fs => $busy_name ); |
763
|
|
|
|
|
|
|
|
764
|
0
|
0
|
|
|
|
|
sysopen my $fh, $busy_name_fs, Fcntl::O_WRONLY | Fcntl::O_CREAT | Fcntl::O_EXCL |
765
|
|
|
|
|
|
|
or $logger -> logdie( 'cannot open %s for writing: %s', |
766
|
|
|
|
|
|
|
$busy_name, |
767
|
|
|
|
|
|
|
$!, |
768
|
|
|
|
|
|
|
); |
769
|
|
|
|
|
|
|
|
770
|
0
|
0
|
|
|
|
|
flock $fh, Fcntl::LOCK_EX |
771
|
|
|
|
|
|
|
or $logger -> logdie( q[can't flock file %s: %s], |
772
|
|
|
|
|
|
|
$busy_name, |
773
|
|
|
|
|
|
|
$! |
774
|
|
|
|
|
|
|
); |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
# For information purposes a bsy file may contain one line of PID information (less than 70 characters). |
777
|
0
|
|
|
|
|
|
printf $fh '%d %s', |
778
|
|
|
|
|
|
|
$$, |
779
|
|
|
|
|
|
|
substr( __FILE__, 0, 70 - 1 - length( $$ ) ); |
780
|
|
|
|
|
|
|
|
781
|
0
|
|
|
|
|
|
eval { |
782
|
0
|
|
|
|
|
|
$sub_ref -> ( $addr, |
783
|
|
|
|
|
|
|
$self, |
784
|
|
|
|
|
|
|
); |
785
|
|
|
|
|
|
|
}; |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
# remove busy first |
788
|
0
|
|
|
|
|
|
close $fh; |
789
|
|
|
|
|
|
|
|
790
|
0
|
0
|
|
|
|
|
unlink $busy_name_fs |
791
|
|
|
|
|
|
|
or $logger -> logwarn( sprintf 'could not unlink %s: %s', |
792
|
|
|
|
|
|
|
$busy_name, |
793
|
|
|
|
|
|
|
$!, |
794
|
|
|
|
|
|
|
); |
795
|
|
|
|
|
|
|
|
796
|
0
|
0
|
|
|
|
|
if ( $@ ) { # something bad happened |
797
|
0
|
|
|
|
|
|
$logger -> logdie( 'referenced sub execution failed: %s', |
798
|
|
|
|
|
|
|
$@, |
799
|
|
|
|
|
|
|
); |
800
|
|
|
|
|
|
|
} |
801
|
|
|
|
|
|
|
|
802
|
0
|
|
|
|
|
|
$self; |
803
|
|
|
|
|
|
|
} |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
=head2 addr_file_to_change |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
Expects arguments: |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
address is going to be dealt with as a FTN::Addr object |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
file type is one of netmail, reference_file, file_request, busy, call, hold, try. |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
If file type is netmail or reference_file, then next parameter should be its flavour: immediate, crash, direct, normal, hold. |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
If optional function reference passed, then it will be called with one parameter - name of the file to process. After that information in internal structure about that file will be updated. |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
Does not deal with busy flag implicitly. Recommended usage is in the function passed to busy_protected_sub. |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
Returns full name of the file to process (might not exists yet though). |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
=cut |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
sub addr_file_to_change { # addr, type ( netmail, file_reference, .. ), [flavour], [ sub_ref( filename ) ]. |
824
|
|
|
|
|
|
|
# figures required filetype name (new or existing) and calls subref with that name. |
825
|
|
|
|
|
|
|
# does not deal with busy implicitly |
826
|
|
|
|
|
|
|
# returns full name of the file to be changed/created |
827
|
0
|
|
|
0
|
1
|
|
my $logger = Log::Log4perl -> get_logger( __PACKAGE__ ); |
828
|
|
|
|
|
|
|
|
829
|
0
|
0
|
|
|
|
|
ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" ); |
830
|
|
|
|
|
|
|
|
831
|
0
|
|
|
|
|
|
my $addr = $self -> _validate_addr( shift ); |
832
|
|
|
|
|
|
|
|
833
|
0
|
|
|
|
|
|
my @flavoured = qw/ netmail |
834
|
|
|
|
|
|
|
reference_file |
835
|
|
|
|
|
|
|
/; |
836
|
|
|
|
|
|
|
|
837
|
0
|
0
|
|
|
|
|
$logger -> logdie( 'no type passed' ) |
838
|
|
|
|
|
|
|
unless @_; |
839
|
|
|
|
|
|
|
|
840
|
0
|
|
|
|
|
|
my $type = shift; |
841
|
|
|
|
|
|
|
|
842
|
0
|
0
|
0
|
|
|
|
$logger -> logdie( sprintf 'incorrect type: %s', |
|
|
0
|
|
|
|
|
|
843
|
|
|
|
|
|
|
defined $type ? $type : 'undef', |
844
|
|
|
|
|
|
|
) |
845
|
|
|
|
|
|
|
unless defined $type |
846
|
|
|
|
|
|
|
&& grep $type eq $_, |
847
|
|
|
|
|
|
|
@flavoured, |
848
|
|
|
|
|
|
|
keys %control_file_extension; |
849
|
|
|
|
|
|
|
|
850
|
0
|
0
|
|
|
|
|
my $filename = $addr -> point ? |
851
|
|
|
|
|
|
|
sprintf( '%08x.', $addr -> point ) |
852
|
|
|
|
|
|
|
: sprintf( '%04x%04x.', |
853
|
|
|
|
|
|
|
$addr -> net, |
854
|
|
|
|
|
|
|
$addr -> node, |
855
|
|
|
|
|
|
|
); |
856
|
|
|
|
|
|
|
|
857
|
0
|
|
|
|
|
|
my $flavoured = grep $type eq $_, @flavoured; |
858
|
0
|
|
|
|
|
|
my $flavour; |
859
|
0
|
0
|
|
|
|
|
if ( $flavoured ) { |
860
|
0
|
0
|
|
|
|
|
$logger -> logdie( 'no flavour passed' ) |
861
|
|
|
|
|
|
|
unless @_; |
862
|
|
|
|
|
|
|
|
863
|
0
|
|
|
|
|
|
$flavour = shift; |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
$logger -> logdie( sprintf 'incorrect flavour: %s', |
866
|
|
|
|
|
|
|
defined $flavour ? $flavour : 'undef', |
867
|
|
|
|
|
|
|
) |
868
|
|
|
|
|
|
|
unless defined $flavour |
869
|
0
|
0
|
0
|
|
|
|
&& exists $flavour_extension{ $flavour }; |
|
|
0
|
|
|
|
|
|
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
$filename .= $type eq $flavoured[ 0 ] ? # netmail |
872
|
|
|
|
|
|
|
$flavour_extension{ $flavour }[ 0 ] |
873
|
0
|
0
|
|
|
|
|
: $flavour_extension{ $flavour }[ 1 ]; |
874
|
|
|
|
|
|
|
} else { |
875
|
0
|
|
|
|
|
|
$filename .= $control_file_extension{ $type }; |
876
|
|
|
|
|
|
|
} |
877
|
|
|
|
|
|
|
|
878
|
0
|
|
|
|
|
|
my $sub_ref; |
879
|
|
|
|
|
|
|
|
880
|
0
|
0
|
|
|
|
|
if ( @_ ) { # possible sub ref |
881
|
0
|
0
|
0
|
|
|
|
$logger -> logdie( 'no valid sub_ref passed' ) |
882
|
|
|
|
|
|
|
unless defined $_[ 0 ] |
883
|
|
|
|
|
|
|
&& 'CODE' eq ref $_[ 0 ]; |
884
|
|
|
|
|
|
|
|
885
|
0
|
|
|
|
|
|
$sub_ref = shift; |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
$self -> scan |
890
|
0
|
0
|
|
|
|
|
unless exists $self -> {scanned}; |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
# check any outdir except our_domain.our_zone for already existing file |
894
|
0
|
|
|
|
|
|
my $dz_out = $self -> _select_domain_zone_dir( $addr -> domain, $addr -> zone ); |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
my @dz_out_with_existing_file = grep length $_ == length $dz_out # to filter out our_domain.our_zone versions |
897
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $_ }{ $addr -> net } |
898
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $_ }{ $addr -> net }{ $addr -> node } |
899
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $_ }{ $addr -> net }{ $addr -> node }{ $addr -> point } |
900
|
|
|
|
|
|
|
&& exists $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $_ }{ $addr -> net }{ $addr -> node }{ $addr -> point }{ $type } |
901
|
|
|
|
|
|
|
&& ( ! $flavoured |
902
|
|
|
|
|
|
|
|| exists $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $_ }{ $addr -> net }{ $addr -> node }{ $addr -> point }{ $type }{ $flavour } |
903
|
|
|
|
|
|
|
), |
904
|
0
|
|
0
|
|
|
|
keys %{ $self -> {scanned}{ $addr -> domain }{ $addr -> zone } }; |
|
0
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
|
906
|
0
|
|
|
|
|
|
my $full_filename; |
907
|
|
|
|
|
|
|
|
908
|
0
|
0
|
|
|
|
|
if ( @dz_out_with_existing_file ) { # file exists |
909
|
0
|
0
|
|
|
|
|
unless ( grep $dz_out eq $_, |
910
|
|
|
|
|
|
|
@dz_out_with_existing_file |
911
|
|
|
|
|
|
|
) { # best domain.zone does not have existing file. let's select best of the worst |
912
|
|
|
|
|
|
|
# first try to find one with the best formatted file |
913
|
|
|
|
|
|
|
my ( $t ) = grep { |
914
|
0
|
|
|
|
|
|
my $r = $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $_ }{ $addr -> net }{ $addr -> node }{ $addr -> point }{ $type }; |
|
0
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
|
916
|
0
|
0
|
|
|
|
|
$r = $r -> { $flavour } |
917
|
|
|
|
|
|
|
if $flavoured; |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
grep $filename eq $_ -> {name}, |
920
|
0
|
|
|
|
|
|
@$r; |
921
|
|
|
|
|
|
|
} @dz_out_with_existing_file; |
922
|
|
|
|
|
|
|
|
923
|
0
|
0
|
|
|
|
|
$dz_out = $t ? $t : $dz_out_with_existing_file[ 0 ]; # or just very first one |
924
|
|
|
|
|
|
|
} |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
# here we've got dz_out with existing file |
927
|
0
|
|
|
|
|
|
my $r = $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $dz_out }{ $addr -> net }{ $addr -> node }{ $addr -> point }{ $type }; |
928
|
|
|
|
|
|
|
|
929
|
0
|
0
|
|
|
|
|
$r = $r -> { $flavour } |
930
|
|
|
|
|
|
|
if $flavoured; |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
$filename = $r -> [ 0 ]{name} |
933
|
|
|
|
|
|
|
unless grep $filename eq $_ -> {name}, # no best file name |
934
|
0
|
0
|
|
|
|
|
@$r; |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
( $full_filename ) = map $_ -> {full_name}, |
937
|
|
|
|
|
|
|
grep $filename eq $_ -> {name}, |
938
|
0
|
|
|
|
|
|
@$r; |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
# and remove it.. |
941
|
0
|
|
|
|
|
|
@$r = grep $filename ne $_ -> {name}, @$r; |
942
|
|
|
|
|
|
|
} else { # no file - create it |
943
|
|
|
|
|
|
|
$full_filename = File::Spec -> catfile( $addr -> point ? |
944
|
|
|
|
|
|
|
$self -> _select_points_dir( $addr -> domain, |
945
|
|
|
|
|
|
|
$addr -> zone, |
946
|
|
|
|
|
|
|
$addr -> net, |
947
|
|
|
|
|
|
|
$addr -> node, |
948
|
|
|
|
|
|
|
) |
949
|
|
|
|
|
|
|
: $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $dz_out }{dir}, |
950
|
0
|
0
|
|
|
|
|
$filename, |
951
|
|
|
|
|
|
|
); |
952
|
|
|
|
|
|
|
} |
953
|
|
|
|
|
|
|
|
954
|
0
|
0
|
|
|
|
|
if ( $sub_ref ) { |
955
|
0
|
|
|
|
|
|
eval { |
956
|
0
|
|
|
|
|
|
$sub_ref -> ( $full_filename ); |
957
|
|
|
|
|
|
|
}; |
958
|
|
|
|
|
|
|
|
959
|
0
|
0
|
|
|
|
|
if ( $@ ) { # something bad happened |
960
|
0
|
|
|
|
|
|
$logger -> logdie( sprintf 'referenced sub execution failed: %s', |
961
|
|
|
|
|
|
|
$@, |
962
|
|
|
|
|
|
|
); |
963
|
|
|
|
|
|
|
} |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
# update file information in internal structure |
966
|
|
|
|
|
|
|
# first remove existing record about file (if it's known) |
967
|
|
|
|
|
|
|
{ |
968
|
0
|
|
|
|
|
|
my $r = $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $dz_out }{ $addr -> net }{ $addr -> node }{ $addr -> point }{ $type }; |
|
0
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
|
970
|
0
|
0
|
|
|
|
|
$r = $r -> { $flavour } |
971
|
|
|
|
|
|
|
if $flavoured; |
972
|
|
|
|
|
|
|
|
973
|
0
|
|
|
|
|
|
my ( $record_idx ) = grep $_ -> {full_name} eq $full_filename, 0 .. $#$r; |
974
|
0
|
0
|
|
|
|
|
splice @$r, $record_idx, 1 |
975
|
|
|
|
|
|
|
if defined $record_idx; |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
# might be a good idea to remove empty parents as well if there was just one file |
978
|
|
|
|
|
|
|
} |
979
|
|
|
|
|
|
|
|
980
|
0
|
0
|
|
|
|
|
if ( -e Encode::encode( locale_fs => $full_filename ) ) { |
981
|
0
|
|
|
|
|
|
my $r = $self -> {scanned}{ $addr -> domain }{ $addr -> zone }{ $dz_out }{ $addr -> net }{ $addr -> node }{ $addr -> point }{ $type }; |
982
|
|
|
|
|
|
|
|
983
|
0
|
0
|
|
|
|
|
$r = $r -> { $flavour } |
984
|
|
|
|
|
|
|
if $flavoured; |
985
|
|
|
|
|
|
|
|
986
|
0
|
|
|
|
|
|
my %file_prop = ( name => $filename, |
987
|
|
|
|
|
|
|
full_name => $full_filename, |
988
|
|
|
|
|
|
|
mstat => ( stat _ )[ 9 ], |
989
|
|
|
|
|
|
|
size => -s _, |
990
|
|
|
|
|
|
|
); |
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
$file_prop{referenced_files} = |
993
|
|
|
|
|
|
|
FTN::Outbound::Reference_file |
994
|
|
|
|
|
|
|
-> new( $file_prop{full_name}, |
995
|
|
|
|
|
|
|
$self -> {reference_file_read_line_transform_sub}, |
996
|
|
|
|
|
|
|
) |
997
|
|
|
|
|
|
|
-> read_existing_file |
998
|
|
|
|
|
|
|
-> referenced_files |
999
|
|
|
|
|
|
|
if $type eq 'reference_file' |
1000
|
|
|
|
|
|
|
&& $file_prop{size} # empty files are empty, right? |
1001
|
0
|
0
|
0
|
|
|
|
&& exists $self -> {reference_file_read_line_transform_sub}; |
|
|
|
0
|
|
|
|
|
1002
|
|
|
|
|
|
|
|
1003
|
0
|
|
|
|
|
|
push @$r, \ %file_prop; |
1004
|
|
|
|
|
|
|
} |
1005
|
|
|
|
|
|
|
} |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
# what to return - just full name or open handle? handle probably better (can update scanned structure, but buffered/unbuffered? access details?) |
1008
|
|
|
|
|
|
|
# let's try full name first |
1009
|
0
|
|
|
|
|
|
$full_filename; |
1010
|
|
|
|
|
|
|
} |
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
1; |