File Coverage

blib/lib/EMDIS/ECS/Config.pm
Criterion Covered Total %
statement 213 225 94.6
branch 89 110 80.9
condition 28 36 77.7
subroutine 15 16 93.7
pod 0 2 0.0
total 345 389 88.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             #
3             # Copyright (C) 2002-2018 National Marrow Donor Program. All rights reserved.
4             #
5             # For a description of this module, please refer to the POD documentation
6             # embedded at the bottom of the file (e.g. perldoc EMDIS::ECS::Config).
7              
8             package EMDIS::ECS::Config;
9              
10 2     2   15629 use Carp;
  2         3  
  2         139  
11 2     2   12 use Cwd;
  2         5  
  2         121  
12 2     2   610 use EMDIS::ECS qw($VERSION is_yes is_no);
  2         5  
  2         248  
13 2     2   15 use File::Basename;
  2         3  
  2         123  
14 2     2   11 use File::Spec::Functions qw(catdir catfile file_name_is_absolute rel2abs);
  2         12  
  2         110  
15 2     2   40 use strict;
  2         6  
  2         79  
16 2     2   978 use Text::ParseWords;
  2         2678  
  2         130  
17 2     2   18 use vars qw($AUTOLOAD %ok_attr);
  2         4  
  2         223  
18              
19             # ----------------------------------------------------------------------
20             # initialize %ok_attr hash with valid attribute names
21             BEGIN
22             {
23 2     2   17 my @attrlist = qw(
24             MSG_PROC MAIL_MRK THIS_NODE T_CHK T_SCN ERR_FILE LOG_FILE ADM_ADDR
25             M_MSG_PROC BCK_DIR ACK_THRES LOG_LEVEL MAIL_LEVEL
26             ECS_BIN_DIR ECS_DAT_DIR ECS_TO_DIR ECS_FROM_DIR ECS_DEBUG
27             NODE_TBL NODE_TBL_LCK T_ADM_DELAY T_ADM_REMIND T_MSG_PROC
28             ADAPTER_CMD ALWAYS_ACK GNU_TAR T_RESEND_DELAY
29             SMTP_HOST SMTP_DOMAIN SMTP_TIMEOUT SMTP_DEBUG SMTP_FROM SMTP_PORT
30             SMTP_USE_SSL SMTP_USE_STARTTLS SMTP_USERNAME SMTP_PASSWORD
31             INBOX_PROTOCOL INBOX_HOST INBOX_PORT INBOX_TIMEOUT INBOX_DEBUG
32             INBOX_FOLDER INBOX_USERNAME INBOX_PASSWORD INBOX_MAX_MSG_SIZE
33             INBOX_DIRECTORY INBOX_USE_SSL INBOX_USE_STARTTLS
34             MSG_PART_SIZE_DFLT
35             GPG_HOMEDIR GPG_KEYID GPG_PASSPHRASE
36             OPENPGP_CMD_ENCRYPT OPENPGP_CMD_DECRYPT
37             PGP_HOMEDIR PGP_KEYID PGP_PASSPHRASE
38             PGP2_CMD_ENCRYPT PGP2_CMD_DECRYPT
39             );
40 2         4 for my $attr (@attrlist)
41             {
42 120         5362 $ok_attr{$attr} = 1;
43             }
44             }
45              
46             # ----------------------------------------------------------------------
47             # use AUTOLOAD method to provide accessor methods
48             # (as described in Perl Cookbook)
49             sub AUTOLOAD
50             {
51 138     138   614 my $this = shift;
52 138         182 my $attr = $AUTOLOAD;
53 138         659 $attr =~ s/.*:://;
54 138 100       640 return unless $attr =~ /[^A-Z]/; # skip DESTROY and all-cap methods
55             # (for EMDIS::ECS::Config, all valid
56             # attribute names contain at least
57             # one underscore)
58 132 50       317 croak "invalid attribute method: ->$attr()" unless $ok_attr{$attr};
59 132         882 return $this->{uc $attr};
60             }
61              
62             # ----------------------------------------------------------------------
63             # Constructor.
64             # Returns EMDIS::ECS::Config reference if successful or error message if
65             # error encountered.
66             sub new
67             {
68 7     7 0 5408 my $class = shift;
69 7         13 my $cfg_file = shift;
70 7         10 my $skip_val = shift;
71 7 50       22 $skip_val = '' unless defined $skip_val;
72 7         12 my $this = {};
73 7         14 bless $this, $class;
74 7 50       17 $cfg_file = 'ecs.cfg' unless defined($cfg_file);
75 7         19 $this->{CFG_FILE} = $cfg_file;
76 7         18754 $this->{CFG_CWD} = cwd(); # remember cwd in case it may be needed
77 7         224 $this->_set_defaults();
78 7 50       29 return $this if $cfg_file eq ''; # default config (used by ecs_setup)
79 7         19 my $err = $this->_read_config();
80 7 100       66 return $err if $err;
81 5 50       15 return $this if $skip_val; # skip validation (used by ecs_setup)
82 5         22 $err = $this->_massage_config();
83 5 50       19 return $err if $err;
84 5         34 $err = $this->_validate_config();
85 5 100       85 return $err if $err;
86 2         30 return $this;
87             }
88              
89             # ----------------------------------------------------------------------
90             # Display configuration data.
91             sub display
92             {
93 0     0 0 0 my $this = shift;
94 0         0 my $fmt = "%-20s %s\n";
95 0         0 print "ECS_CFG\n";
96 0         0 print "---------------------------------------------------------------\n";
97 0         0 for my $attr (sort keys %$this) {
98 0         0 my $value = $this->{$attr};
99 0 0       0 $value = '********' if $attr =~ /PASSWORD|PASSPHRASE/i;
100 0         0 printf $fmt, $attr, $value;
101             }
102             }
103              
104             # ----------------------------------------------------------------------
105             # Parse config file data.
106             # Returns empty string if successful or error message if error encountered.
107             sub _read_config
108             {
109 7     7   12 my $this = shift;
110              
111             # read config file
112 7         24 my $err = '';
113 7 100       331 if(not open CONFIG, "< $this->{CFG_FILE}")
114             {
115 1         24 return "Unable to open config file '$this->{CFG_FILE}': $!";
116             }
117 6         176 while()
118             {
119 151         242 chomp; # trim EOL character(s)
120 151         315 s/^\s+//; # trim leading whitespace
121 151         454 s/\s+$//; # trim trailing whitespace
122 151 100       294 next unless length; # skip blank line
123 145 100       388 next if /^#/; # skip comment line
124 117         171 my @fields = ();
125 117         306 my @tokens = split '\|'; # split on pipe ('|') delimiter
126 117         204 for my $token (@tokens)
127             {
128 351 50 66     954 if($#fields >= 0 and $fields[$#fields] =~ /\\$/)
129             {
130             # rejoin tokens separated by escaped pipe ('\|')
131 0         0 chop($fields[$#fields]);
132 0         0 $fields[$#fields] .= "|$token";
133             }
134             else
135             {
136 351         630 push(@fields, $token);
137             }
138             }
139             # trim leading & trailing whitespace
140 117         183 @fields = map { s/^\s+//; s/\s+$//; $_; } @fields;
  351         734  
  351         880  
  351         727  
141 117         218 my ($attr, $value, $comment) = @fields;
142 117 100       245 if($ok_attr{$attr})
143             {
144             # store value
145 116         654 $this->{$attr} = $value;
146             }
147             else
148             {
149 1         65 $err .=
150             "Unexpected input '$attr' at $this->{CFG_FILE} line $.\n";
151             }
152             }
153 6         61 close CONFIG;
154 6 100       20 if($err)
155             {
156 1         13 return $err .
157             "Error(s) encountered while attempting to process " .
158             "$this->{CFG_FILE}.";
159             }
160              
161 5         19 return '';
162             }
163              
164             # ----------------------------------------------------------------------
165             # Massage config data.
166             # Returns empty string if successful or error message if error encountered.
167             sub _massage_config
168             {
169 5     5   12 my $this = shift;
170              
171             # initialize
172 5         196 my $script_dir = dirname($0);
173 5 50       111 $script_dir = rel2abs($script_dir)
174             unless file_name_is_absolute($script_dir);
175 5         554 my $config_dir = dirname($this->{CFG_FILE});
176 5 50       18 $config_dir = rel2abs($config_dir)
177             unless file_name_is_absolute($config_dir);
178              
179             # parse some special tokens
180 5         281 for my $attr (keys %ok_attr)
181             {
182 332 100       550 if(exists $this->{$attr})
183             {
184 251         371 my $value = $this->{$attr};
185 251         354 $value =~ s/__SCRIPT_DIR__/$script_dir/;
186 251         321 $value =~ s/__CONFIG_DIR__/$config_dir/;
187 251         396 $this->{$attr} = $value;
188             }
189             }
190              
191             # prepend ECS_DAT_DIR to non-absolute file/path names
192 5         23 for my $attr (qw(ERR_FILE GPG_HOMEDIR LOG_FILE NODE_TBL NODE_TBL_LCK
193             PGP_HOMEDIR ECS_TO_DIR ECS_FROM_DIR))
194             {
195             $this->{$attr} = catfile($this->{ECS_DAT_DIR}, $this->{$attr})
196             if exists($this->{$attr})
197             and not ($this->{$attr} eq '')
198 40 100 66     417 and not file_name_is_absolute($this->{$attr});
      100        
199             }
200              
201             # prepend ECS_BIN_DIR to non-absolute executable command names
202 5         12 for my $attr (qw(MSG_PROC M_MSG_PROC))
203             {
204             $this->{$attr} = catfile($this->{ECS_BIN_DIR}, $this->{$attr})
205             if exists($this->{$attr})
206 10 50 33     157 and not file_name_is_absolute($this->{$attr});
207             }
208              
209             # compute derived values
210 5         55 $this->{ECS_TMP_DIR} = catdir($this->{ECS_DAT_DIR}, 'tmp');
211 5 100 66     26 if ( ! defined($this->{ECS_TO_DIR}) || $this->{ECS_TO_DIR} eq '')
212             {
213 4         15 $this->{ECS_DRP_DIR} = catdir($this->{ECS_DAT_DIR}, 'maildrop');
214             }
215             else
216             {
217 1         13 $this->{ECS_DRP_DIR} = $this->{ECS_TMP_DIR};
218             }
219 5         39 $this->{ECS_MBX_DIR} = catdir($this->{ECS_DAT_DIR}, 'mboxes');
220 5         17 $this->{ECS_MBX_IN_DIR} = catdir($this->{ECS_MBX_DIR}, 'in');
221 5         15 $this->{ECS_MBX_IN_FML_DIR} = catdir($this->{ECS_MBX_DIR}, 'in_fml');
222 5         23 $this->{ECS_MBX_OUT_DIR} = catdir($this->{ECS_MBX_DIR}, 'out');
223 5         27 $this->{ECS_MBX_TRASH_DIR} = catdir($this->{ECS_MBX_DIR}, 'trash');
224 5         40 $this->{ECS_MBX_STORE_DIR} = catdir($this->{ECS_MBX_DIR}, 'store');
225 5         12 for my $attr (qw(ECS_TMP_DIR ECS_DRP_DIR ECS_MBX_DIR ECS_MBX_IN_DIR
226             ECS_MBX_IN_FML_DIR ECS_MBX_OUT_DIR ECS_MBX_TRASH_DIR
227             ECS_MBX_STORE_DIR))
228             {
229 40         71 $ok_attr{$attr} = 1;
230             }
231              
232             # parse more special tokens
233 5         42 for my $attr (keys %ok_attr)
234             {
235 340 100       551 if(exists $this->{$attr})
236             {
237 291         397 my $value = $this->{$attr};
238 291         393 $value =~ s/__MAILDROP_DIR__/$this->{ECS_DRP_DIR}/;
239 291         504 $this->{$attr} = $value;
240             }
241             }
242              
243             # if indicated, assign SMTP_PORT default value
244 5 100       24 if(not defined($this->{SMTP_PORT})) {
245 4         12 $this->{SMTP_PORT} = 25;
246 4 100       61 $this->{SMTP_PORT} = 465 if is_yes($this->{SMTP_USE_SSL});
247 4 100       11 $this->{SMTP_PORT} = 587 if is_yes($this->{SMTP_USE_STARTTLS});
248             }
249              
250             # if indicated, assign INBOX_PORT default value
251 5 100       12 if(not defined($this->{INBOX_PORT})) {
252 4         8 for($this->{INBOX_PROTOCOL})
253             {
254 4 100       12 /POP3/ and do {
255 2         3 $this->{INBOX_PORT} = 110;
256 2 50       4 $this->{INBOX_PORT} = 995 if is_yes($this->{INBOX_USE_SSL});
257             };
258 4 100       12 /IMAP/ and do {
259 1         12 $this->{INBOX_PORT} = 143;
260 1 50       11 $this->{INBOX_PORT} = 993 if is_yes($this->{INBOX_USE_SSL});
261             };
262             }
263             }
264              
265 5         13 return '';
266             }
267              
268             # ----------------------------------------------------------------------
269             # Assign default values to configuration settings.
270             # Note: no default value for THIS_NODE, ADM_ADDR, ADAPTER_CMD, SMTP_DOMAIN,
271             # SMTP_FROM, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, INBOX_PORT,
272             # INBOX_FOLDER, INBOX_USERNAME, INBOX_PASSWORD, GPG_HOMEDIR, GPG_KEYID,
273             # GPG_PASSPHRASE, PGP_HOMEDIR, PGP_KEYID, PGP_PASSPHRASE
274             sub _set_defaults
275             {
276 7     7   33 my $this = shift;
277 7         66 $this->{MSG_PROC} = "ecs_proc_msg";
278 7         76 $this->{MAIL_MRK} = "EMDIS";
279 7         59 $this->{T_CHK} = "7200";
280 7         71 $this->{T_SCN} = "3600";
281 7         807 my $basename = basename($0); # default; use magic logfile name
282 7         44 $this->{ERR_FILE} = "$basename.err";
283 7         86 $this->{LOG_FILE} = "$basename.log";
284 7         57 $this->{M_MSG_PROC} = "ecs_proc_meta";
285 7         18 $this->{BCK_DIR} = "NONE";
286 7         26 $this->{ACK_THRES} = "100";
287 7         47 $this->{ALWAYS_ACK} = "NO";
288 7         39 $this->{GNU_TAR} = "/usr/bin/tar";
289 7         41 $this->{ECS_BIN_DIR} = "__SCRIPT_DIR__";
290 7         70 $this->{ECS_DAT_DIR} = "__CONFIG_DIR__";
291 7         76 $this->{ECS_DEBUG} = "0";
292 7         71 $this->{NODE_TBL} = "node_tbl.dat";
293 7         23 $this->{NODE_TBL_LCK} = "node_tbl.lock";
294 7         13 $this->{T_ADM_DELAY} = "0";
295 7         23 $this->{T_ADM_REMIND} = "86400";
296 7         63 $this->{T_MSG_PROC} = "3600";
297 7         23 $this->{T_RESEND_DELAY} = "14400";
298 7         39 $this->{LOG_LEVEL} = 1;
299 7         21 $this->{MAIL_LEVEL} = 2;
300 7         37 $this->{MSG_PART_SIZE_DFLT} = "1073741824";
301 7         45 $this->{SMTP_HOST} = "smtp";
302 7         42 $this->{SMTP_TIMEOUT} = "60";
303 7         18 $this->{SMTP_DEBUG} = "0";
304 7         38 $this->{SMTP_USE_SSL} = "NO";
305 7         15 $this->{SMTP_USE_STARTTLS} = "NO";
306 7         10 $this->{INBOX_PROTOCOL} = "POP3";
307 7         33 $this->{INBOX_HOST} = "mail";
308 7         14 $this->{INBOX_FOLDER} = "INBOX";
309 7         21 $this->{INBOX_TIMEOUT} = "60";
310 7         10 $this->{INBOX_DEBUG} = "0";
311 7         18 $this->{INBOX_USE_SSL} = "NO";
312 7         9 $this->{INBOX_USE_STARTTLS} = "NO";
313 7         11 $this->{INBOX_MAX_MSG_SIZE} = "1048576";
314 7         47 $this->{OPENPGP_CMD_ENCRYPT} = '/usr/local/bin/gpg --armor --batch ' .
315             '--charset ISO-8859-1 --force-mdc --logger-fd 1 --openpgp ' .
316             '--output __OUTPUT__ --passphrase-fd 0 --quiet ' .
317             '--recipient __RECIPIENT__ --recipient __SELF__ --yes ' .
318             '--sign --local-user __SELF__ --encrypt __INPUT__';
319 7         45 $this->{OPENPGP_CMD_DECRYPT} = '/usr/local/bin/gpg --batch ' .
320             '--charset ISO-8859-1 --logger-fd 1 --openpgp --output __OUTPUT__ ' .
321             '--passphrase-fd 0 --quiet --yes --decrypt __INPUT__';
322 7         45 $this->{PGP2_CMD_ENCRYPT} = '/usr/local/bin/pgp +batchmode +verbose=0 ' .
323             '+force +CharSet=latin1 +ArmorLines=0 -o __OUTPUT__ ' .
324             '-u __SELF__ -eats __INPUT__ __RECIPIENT__ __SELF__';
325 7         53 $this->{PGP2_CMD_DECRYPT} = '/usr/local/bin/pgp +batchmode +verbose=0 ' .
326             '+force +CharSet=latin1 -o __OUTPUT__ __INPUT__';
327             }
328              
329             # ----------------------------------------------------------------------
330             # Do a few sanity checks on the configuration data.
331             # Returns empty string if successful or error message if error encountered.
332             sub _validate_config
333             {
334 5     5   15 my $this = shift;
335 5         9 my @errors = ();
336 5         78 my @required_attrlist = qw(
337             MSG_PROC MAIL_MRK THIS_NODE T_CHK T_SCN ERR_FILE LOG_FILE ADM_ADDR
338             M_MSG_PROC BCK_DIR ACK_THRES
339             ECS_BIN_DIR ECS_DAT_DIR ECS_DEBUG
340             NODE_TBL NODE_TBL_LCK T_ADM_REMIND T_MSG_PROC
341             SMTP_HOST SMTP_DOMAIN SMTP_TIMEOUT SMTP_DEBUG SMTP_FROM
342             INBOX_PROTOCOL INBOX_HOST INBOX_TIMEOUT INBOX_DEBUG
343             INBOX_MAX_MSG_SIZE
344             MSG_PART_SIZE_DFLT
345             );
346              
347             # check for required values that are undefined
348 5         30 for my $attr (@required_attrlist)
349             {
350             push(@errors, "$attr not defined.")
351 145 100       265 unless exists($this->{$attr});
352             }
353              
354             # validate INBOX_PROTOCOL
355            
356             # username/password not needed for DIRECTORY inbox
357 5 50       12 if($this->{INBOX_PROTOCOL} !~ /DIRECTORY/i)
358             {
359 5         7 for my $attr (qw( INBOX_USERNAME INBOX_PASSWORD))
360             {
361             push(@errors, "$attr not defined.")
362 10 100       23 unless exists($this->{$attr});
363             }
364             }
365              
366 5 100       44 if($this->{INBOX_PROTOCOL} =~ /IMAP/i)
    100          
    50          
367             {
368 2         8 $this->{INBOX_PROTOCOL} = 'IMAP'; # force uppercase
369             push(@errors,
370             "INBOX_FOLDER not defined, but is required for IMAP protocol.")
371 2 50       8 unless defined($this->{INBOX_FOLDER});
372             }
373             elsif($this->{INBOX_PROTOCOL} =~ /POP3/i)
374             {
375 2         4 $this->{INBOX_PROTOCOL} = 'POP3'; # force uppercase
376             }
377             elsif($this->{INBOX_PROTOCOL} =~ /DIRECTORY/i)
378             {
379 0         0 $this->{INBOX_PROTOCOL} = 'DIRECTORY'; # force uppercase
380             push(@errors, "INBOX_DIRECTORY not defined, but is required for " .
381             "DIRECTORY protocol.")
382 0 0       0 unless defined($this->{INBOX_DIRECTORY});
383             }
384             else
385             {
386 1         17 push(@errors,
387             "Unrecognized INBOX_PROTOCOL: $this->{INBOX_PROTOCOL}");
388             }
389              
390             # check whether an encryption method is configured
391 5 50 66     20 if(!exists($this->{GPG_HOMEDIR}) && !exists($this->{PGP_HOMEDIR}))
392             {
393 1         3 push(@errors, "No encryption method configured. Need to " .
394             "configure either GPG_HOMEDIR or PGP_HOMEDIR, etc.");
395             }
396              
397             # check OpenPGP encryption setup
398 5 100       10 if(exists($this->{GPG_HOMEDIR}))
399             {
400 4         19 for my $attr (qw(GPG_HOMEDIR GPG_KEYID GPG_PASSPHRASE
401             OPENPGP_CMD_ENCRYPT OPENPGP_CMD_DECRYPT))
402             {
403             push(@errors, "$attr not defined, but is required for OpenPGP " .
404             "encryption setup (GPG_HOMEDIR = " .
405             $this->{GPG_HOMEDIR} . ").")
406 20 100       55 unless exists($this->{$attr});
407             }
408             }
409              
410             # check PGP encryption setup
411 5 100       11 if(exists($this->{PGP_HOMEDIR}))
412             {
413 3         13 for my $attr (qw(PGP_HOMEDIR PGP_KEYID PGP_PASSPHRASE
414             PGP2_CMD_ENCRYPT PGP2_CMD_DECRYPT))
415             {
416             push(@errors, "$attr not defined, but is required for PGP2 " .
417             "encryption setup (PGP_HOMEDIR = " .
418             $this->{PGP_HOMEDIR} . ").")
419 15 100       44 unless exists($this->{$attr});
420             }
421             }
422              
423             # validate T_CHK
424 5 100       16 if($this->{T_CHK} <= 0)
425             {
426 1         8 push(@errors,
427             "T_CHK ($this->{T_CHK}) is required to be greater than zero.");
428             }
429              
430             # validate T_SCN
431 5 100       13 if($this->{T_SCN} <= 0)
432             {
433 1         9 push(@errors,
434             "T_SCN ($this->{T_SCN}) is required to be greater than zero.");
435             }
436              
437             # validate T_ADM_REMIND
438 5 100       13 if($this->{T_ADM_REMIND} <= 0)
439             {
440 1         5 push(@errors,
441             "T_ADM_REMIND ($this->{T_ADM_REMIND}) is required " .
442             "to be greater than zero.");
443             }
444              
445             # validate T_MSG_PROC
446 5 100       30 if($this->{T_MSG_PROC} <= 0)
447             {
448 1         10 push(@errors,
449             "T_MSG_PROC ($this->{T_MSG_PROC}) is required " .
450             "to be greater than zero.");
451             }
452              
453             # validate YES/NO values
454 5         9 for my $name (qw(ALWAYS_ACK INBOX_USE_SSL INBOX_USE_STARTTLS SMTP_USE_SSL SMTP_USE_STARTTLS))
455             {
456 25 100 66     119 if(exists $this->{$name} and not is_yes($this->{$name})
      100        
457             and not is_no($this->{$name}))
458             {
459             push(@errors, "Unrecognized $name (YES/NO) value: " .
460 5         19 $this->{$name});
461             }
462             }
463              
464 5 100 100     19 if(is_yes($this->{INBOX_USE_SSL})
465             and is_yes($this->{INBOX_USE_STARTTLS}))
466             {
467 1         8 push(@errors, "INBOX_USE_SSL and INBOX_USE_STARTTLS " .
468             "are both selected, but they are mutually exclusive.");
469             }
470              
471 5 100 100     13 if(is_yes($this->{SMTP_USE_SSL})
472             and is_yes($this->{SMTP_USE_STARTTLS}))
473             {
474 1         7 push(@errors, "SMTP_USE_SSL and SMTP_USE_STARTTLS " .
475             "are both selected, but they are mutually exclusive.");
476             }
477              
478             # check whether directories exist
479 5         42 my @dirs = qw(ECS_BIN_DIR ECS_DAT_DIR ECS_TMP_DIR ECS_MBX_DIR
480             ECS_MBX_IN_DIR ECS_MBX_IN_FML_DIR ECS_MBX_OUT_DIR
481             ECS_MBX_TRASH_DIR ECS_MBX_STORE_DIR GPG_HOMEDIR
482             PGP_HOMEDIR);
483 5 50       16 push(@dirs, 'BCK_DIR') if($this->{BCK_DIR} ne 'NONE');
484             push(@dirs, 'ECS_DRP_DIR')
485             if( ! defined($this->{ECS_TO_DIR})
486 5 100 66     33 || $this->{ECS_TO_DIR} eq '');
487 5         10 for my $dir (@dirs)
488             {
489 59 100 100     819 if(exists $this->{$dir} and not -d $this->{$dir})
490             {
491 8         36 push(@errors,
492             "$dir ($this->{$dir}) directory not found.");
493             }
494             }
495              
496             # return error messages, if any
497 5 100       16 if($#errors >= 0)
498             {
499 3         9 push(@errors,
500             "Error(s) detected in configuration file $this->{CFG_FILE}");
501 3         5 push(@errors, "Fatal configuration error(s) encountered.\n");
502 3         34 return "\n" . join("\n", @errors);
503             }
504 2         11 return '';
505             }
506              
507             1;
508              
509             __DATA__