source: npl/mailserver/dspam/dspam-3.10.2/contrib/dspam_alias_retraining/dspam-retrain-forward.pl

Last change on this file was c5c522c, checked in by Edwin Eefting <edwin@datux.nl>, 8 years ago

initial commit, transferred from cleaned syn3 svn tree

  • Property mode set to 100755
File size: 18.1 KB
Line 
1#!/usr/bin/perl
2###
3# $Id: dspam-retrain-forward.pl,v0.6 2011/08/11 22:20:04 sbajic Exp $
4#
5# Copyright 2006-2011, Stevan Bajic <stevan@bajic.ch>, all rights reserved.
6# Distributed under the terms of the GNU Affero General Public License v3
7#
8# Purpose: Script for retraining DSPAM
9###
10
11use strict;
12use vars qw { %CONFIG %ERROR_CODE %SIGNATURES @MESSAGE };
13
14#
15# Default DSPAM enviroment
16#
17$CONFIG{'BIN_DIR'}              = "/usr/bin";         # Path where the DSPAM binary is located.
18$CONFIG{'FULL_EMAIL'}           = 1;                  # Use full email as username or only the
19                                                      # local part (that's everything before the @)
20                                                      # for DSPAM. 1 = yes, 0 = no
21$CONFIG{'HEADERS_ONLY'}         = 1;                  # If you wish to parse the X-DSPAM-Signature
22                                                      # header, then set this to 1 (=yes) or set it
23                                                      # to 0 (=no) if you only wish to parse the
24                                                      # signature found in body of the message.
25                                                      # The headers will be parsed in case insensitive
26                                                      # mode, since Outlook 2003 coverts the DSPAM
27                                                      # header into lower case when forwarding mails.
28                                                      # Headers will be even processed if they are in
29                                                      # attachments.
30$CONFIG{'BODIES_ONLY'}          = 0;                  # If you wish to parse the !DSPAM:...! signature
31                                                      # in the body, then set this to 1 (=yes) or set
32                                                      # it to 0 (=no) if you only wish to parse the
33                                                      # signature found in header of the message.
34                                                      # Bodies will be even processed if they are in
35                                                      # attachments.
36$CONFIG{'FIRST_SIGNATURE_ONLY'} = 0;                  # Should the retrain script process the complete
37                                                      # mail and search for all the signature(s) it can
38                                                      # find or should it stop after the first signature
39                                                      # is find? Set this value to 0 (=no) to process/
40                                                      # parse the complete mail and search for
41                                                      # signature(s) or set it to 1 (=yes) to stop
42                                                      # processing the mail after the first found
43                                                      # signature.
44$CONFIG{'SKIP_FIRST'}           = 0;                  # Should the retrain script skip the first
45                                                      # signature it finds? Set this value to 0 (=no) or
46                                                      # set it to 1 (=yes).
47$CONFIG{'DEBUG'}                = 0;                  # 0 (=no) and 1 (=yes). Setting this to 1 (=yes)
48                                                      # will not send anything to DSPAM, but print out
49                                                      # some info about what the script would execute
50                                                      # when running without the debug flag.
51
52### DO NOT CONFIGURE ANYTHING BELOW THIS LINE
53
54#
55# Default values
56#
57$CONFIG{'DSPAM_BIN'}            = $CONFIG{'BIN_DIR'} . "/dspam";
58$CONFIG{'MODE'}                 = "";
59$CONFIG{'CLASS'}                = "";
60$CONFIG{'SOURCE'}               = "";
61$CONFIG{'SENDER'}               = "";
62$CONFIG{'RECIPIENT'}            = "";
63$CONFIG{'USER'}                 = "";
64$CONFIG{'PARAMETERS'}           = "";
65
66#
67# Exit codes from <sysexits.h>
68#
69$ERROR_CODE{'EX_OK'}            = 0;                  # successful termination
70$ERROR_CODE{'EX_USAGE'}         = 64;                 # command line usage error
71$ERROR_CODE{'EX_UNAVAILABLE'}   = 69;                 # service unavailable
72$ERROR_CODE{'EX_TEMPFAIL'}      = 75;                 # temp failure; user is invited to retry
73$ERROR_CODE{'EX_CONFIG'}        = 78;                 # configuration error
74
75#
76# Get arguments
77#
78while ($_ = $ARGV[0], /^\-\-/) {
79  last if /^\-\-$/;
80  if ($ARGV[0] =~ /^\-\-help$/) {
81    print STDERR "Usage: $0\n";
82    print STDERR "  [--help]\n";
83    print STDERR "    This help screen.\n";
84    print STDERR "  [--debug=[yes|no]]\n";
85    print STDERR "    Turn on debugging. No action will be performed, just printed.\n";
86    print STDERR "  [--user username]]\n";
87    print STDERR "    User name to use for training.\n";
88    print STDERR "  [--client]\n";
89    print STDERR "    To run in client mode.\n";
90    print STDERR "  [--class=spam|innocent]\n";
91    print STDERR "    Class used for the training.\n";
92    print STDERR "  [--mode=teft|toe|tum|notrain|unlearn]\n";
93    print STDERR "    Configures the training mode to be used for this process, overriding\n";
94    print STDERR "    any defaults in dspam.conf or the preference extension.\n";
95    print STDERR "  [--source=error|corpus|inoculation]\n";
96    print STDERR "    The source tells DSPAM how to learn the message being presented.\n";
97    print STDERR "  [--full-email=[yes|no]]\n";
98    print STDERR "    Use the full email address as username or just the local part.\n";
99    print STDERR "  [--headers-only=[yes|no]]\n";
100    print STDERR "    Process only headers (aka: X-DSPAM-Signature) of the message.\n";
101    print STDERR "  [--bodies-only=[yes|no]]\n";
102    print STDERR "    Process only bodies (aka: !DSPAM:xxx!) of the message.\n";
103    print STDERR "  [--first-only=[yes|no]]\n";
104    print STDERR "    Only use the first found DSPAM signature and skip the others.\n";
105    print STDERR "  [--skip-first=[yes|no]]\n";
106    print STDERR "    Skip the first found signature.\n";
107    print STDERR "  [--mail-from=sender-address]\n";
108    print STDERR "    Set the MAIL FROM sent on delivery of the message (only valid for\n";
109    print STDERR "    SMTP/LMTP delivery).\n";
110    print STDERR "  [--rcpt-to=recipient-address]\n";
111    print STDERR "    Define the RCPT TO which will be used for the delivery (only valid\n";
112    print STDERR "    for SMTP/LMTP delivery).\n";
113    print STDERR "  [--bin-dir=path]\n";
114    print STDERR "    Define the path where dspam/dspamc is located.\n";
115    exit $ERROR_CODE{'EX_USAGE'};
116  } elsif ($ARGV[0] =~ /^\-\-debug$/) {
117    $CONFIG{'DEBUG'} = 1;
118    shift;
119  } elsif ($ARGV[0] =~ /^\-\-debug=(.*)$/) {
120    if ($1 =~ /^(yes|no)$/i) {
121      if ($1 =~ /^yes$/i) {
122        $CONFIG{'DEBUG'} = 1;
123      } else {
124        $CONFIG{'DEBUG'} = 0;
125      }
126      shift;
127    } else {
128      print STDERR "Debug must be either yes or no.\n" if ($CONFIG{'DEBUG'} == 1);
129      exit $ERROR_CODE{'EX_USAGE'};
130    }
131  } elsif ($ARGV[0] =~ /^\-\-client$/) {
132    $CONFIG{'DSPAM_BIN'} = $CONFIG{'BIN_DIR'} . "/dspamc";
133    shift;
134  } elsif ($ARGV[0] =~ /^(\-\-user)$/) {
135    if (defined($ARGV[1])) {
136      $CONFIG{'USER'} = $ARGV[1];
137      shift;
138    } else {
139      print STDERR "You need to specify a username for the retraining.\n" if ($CONFIG{'DEBUG'} == 1);
140      exit $ERROR_CODE{'EX_USAGE'};
141    }
142    shift;
143  } elsif ($ARGV[0] =~ /^\-\-class=(.+)$/) {
144    $CONFIG{'CLASS'} = $1;
145    shift;
146  } elsif ($ARGV[0] =~ /^\-\-mode=(.+)$/) {
147    $CONFIG{'MODE'} = $1;
148    shift;
149  } elsif ($ARGV[0] =~ /^\-\-source=(.+)$/) {
150    $CONFIG{'SOURCE'} = $1;
151    shift;
152  } elsif ($ARGV[0] =~ /^\-\-full\-email$/) {
153    $CONFIG{'FULL_EMAIL'} = 1;
154    shift;
155  } elsif ($ARGV[0] =~ /^\-\-full\-email=(.*)$/) {
156    if ($1 =~ /^(yes|no)$/i) {
157      if ($1 =~ /^yes$/i) {
158        $CONFIG{'FULL_EMAIL'} = 1;
159      } else {
160        $CONFIG{'FULL_EMAIL'} = 0;
161      }
162    } else {
163      print STDERR "Full email must be either yes or no.\n" if ($CONFIG{'DEBUG'} == 1);
164      exit $ERROR_CODE{'EX_USAGE'};
165    }
166    shift;
167  } elsif ($ARGV[0] =~ /^\-\-headers\-only$/) {
168    $CONFIG{'HEADERS_ONLY'} = 1;
169    shift;
170  } elsif ($ARGV[0] =~ /^\-\-headers\-only=(.*)$/) {
171    if ($1 =~ /^(yes|no)$/i) {
172      if ($1 =~ /^yes$/i) {
173        $CONFIG{'HEADERS_ONLY'} = 1;
174      } else {
175        $CONFIG{'HEADERS_ONLY'} = 0;
176      }
177    } else {
178      print STDERR "Header only must be either yes or no.\n" if ($CONFIG{'DEBUG'} == 1);
179      exit $ERROR_CODE{'EX_USAGE'};
180    }
181    shift;
182  } elsif ($ARGV[0] =~ /^\-\-bodies\-only$/) {
183    $CONFIG{'BODIES_ONLY'} = 1;
184    shift;
185  } elsif ($ARGV[0] =~ /^\-\-bodies\-only=(.*)$/) {
186    if ($1 =~ /^(yes|no)$/i) {
187      if ($1 =~ /^yes$/i) {
188        $CONFIG{'BODIES_ONLY'} = 1;
189      } else {
190        $CONFIG{'BODIES_ONLY'} = 0;
191      }
192    } else {
193      print STDERR "Bodies only must be either yes or no.\n" if ($CONFIG{'DEBUG'} == 1);
194      exit $ERROR_CODE{'EX_USAGE'};
195    }
196    shift;
197  } elsif ($ARGV[0] =~ /^\-\-first\-only$/) {
198    $CONFIG{'FIRST_SIGNATURE_ONLY'} = 1;
199    shift;
200  } elsif ($ARGV[0] =~ /^\-\-first\-only=(.*)$/) {
201    if ($1 =~ /^(yes|no)$/i) {
202      if ($1 =~ /^yes$/i) {
203        $CONFIG{'FIRST_SIGNATURE_ONLY'} = 1;
204      } else {
205        $CONFIG{'FIRST_SIGNATURE_ONLY'} = 0;
206      }
207    } else {
208      print STDERR "First match only must be either yes or no.\n" if ($CONFIG{'DEBUG'} == 1);
209      exit $ERROR_CODE{'EX_USAGE'};
210    }
211    shift;
212  } elsif ($ARGV[0] =~ /^\-\-skip\-first$/) {
213    $CONFIG{'SKIP_FIRST'} = 1;
214    shift;
215  } elsif ($ARGV[0] =~ /^\-\-skip\-first=(.*)$/) {
216    if ($1 =~ /^(yes|no)$/i) {
217      if ($1 =~ /^yes$/i) {
218        $CONFIG{'SKIP_FIRST'} = 1;
219      } else {
220        $CONFIG{'SKIP_FIRST'} = 0;
221      }
222    } else {
223      print STDERR "Skip fist must be either yes or no.\n" if ($CONFIG{'DEBUG'} == 1);
224      exit $ERROR_CODE{'EX_USAGE'};
225    }
226    shift;
227  } elsif ($ARGV[0] =~ /^\-\-mail\-from=(.+)$/) {
228    $CONFIG{'SENDER'} = $1;
229    shift;
230  } elsif ($ARGV[0] =~ /^(\-\-rcpt\-to)$/) {
231    if (defined($ARGV[1])) {
232      $CONFIG{'RECIPIENT'} = $ARGV[1];
233      shift;
234    } else {
235      print STDERR "You need to specify a recipient when using --rcpt-to switch.\n" if ($CONFIG{'DEBUG'} == 1);
236      exit $ERROR_CODE{'EX_USAGE'};
237    }
238    shift;
239  } elsif ($ARGV[0] =~ /^\-\-bin\-dir=(.*)$/) {
240    if (-d "$1") {
241      $CONFIG{'BIN_DIR'} = $1;
242      $CONFIG{'DSPAM_BIN'} = $CONFIG{'BIN_DIR'} . "/dspam";
243    } else {
244      print STDERR "The directory '" . $CONFIG{'BIN_DIR'} . "' does not exist.\n" if ($CONFIG{'DEBUG'} == 1);
245      exit $ERROR_CODE{'EX_USAGE'};
246    }
247    shift;
248  } else {
249    shift;
250  }
251}
252
253#
254# Do some error checking
255#
256if ($CONFIG{'HEADERS_ONLY'} == 1 && $CONFIG{'BODIES_ONLY'} == 1) {
257  print STDERR "You can not force header only AND body only processing at the same time.\n" if ($CONFIG{'DEBUG'} == 1);
258  exit $ERROR_CODE{'EX_USAGE'};
259}
260
261#
262# Do not continue if DSPAM binary does not exist
263#
264if (($CONFIG{'DSPAM_BIN'} eq "") || !(-e $CONFIG{'DSPAM_BIN'}) || !(-r $CONFIG{'DSPAM_BIN'})) {
265  print STDERR "ERROR: DSPAM binary '" . $CONFIG{'DSPAM_BIN'} . "' does not exist.\n" if ($CONFIG{'DEBUG'} == 1);
266  exit $ERROR_CODE{'EX_USAGE'};
267}
268
269#
270# Process username for DSPAM
271#
272if ($CONFIG{'USER'} eq "") {
273  if ($CONFIG{'RECIPIENT'} ne "") {
274    if ($CONFIG{'RECIPIENT'} =~ /^(ham|(no[nt]?)?spam)\-?(.+)$/) {
275      $CONFIG{'USER'} = $3;
276    }
277    if ($CONFIG{'RECIPIENT'} =~ /^(ham|(no[nt]?)spam)\-?(.+)$/) {
278      $CONFIG{'CLASS'} = "innocent";
279    } elsif ($CONFIG{'RECIPIENT'} =~ /^spam\-?(.+)$/) {
280      $CONFIG{'CLASS'} = "spam";
281    }
282  } elsif ($CONFIG{'SENDER'} =~ /^(.+)$/) {
283    $CONFIG{'USER'} = $CONFIG{'SENDER'};
284  } else {
285    $CONFIG{'USER'} = (getpwuid($<))[0];
286  }
287}
288if (($CONFIG{'FULL_EMAIL'} == 0) && ($CONFIG{'USER'} =~ /^(.+)@(.+)$/)) {
289  $CONFIG{'USER'} = $1;
290}
291if ($CONFIG{'USER'} eq "") {
292  print STDERR "ERROR: Can't determine user.\n" if ($CONFIG{'DEBUG'} == 1);
293  exit $ERROR_CODE{'EX_TEMPFAIL'};
294}
295
296#
297# Check if class is known by DSPAM
298#
299if ($CONFIG{'CLASS'} eq "") {
300  print STDERR "ERROR: Class can not be empty.\n" if ($CONFIG{'DEBUG'} == 1);
301  exit $ERROR_CODE{'EX_USAGE'};
302} elsif ($CONFIG{'CLASS'} !~ /^(spam|innocent)$/) {
303  open(DSPAM, "$CONFIG{'DSPAM_BIN'} --help 2>&1|");
304  while(<DSPAM>) {
305    chomp;
306    if (/^.*\-\-class=\[([^\]]+).*$/) {
307      if ($CONFIG{'CLASS'} !~ /^($1)$/) {
308        close(DSPAM);
309        print STDERR "ERROR: Class '" . $CONFIG{'CLASS'} . "' is not known by DSPAM.\n" if ($CONFIG{'DEBUG'} == 1);
310        exit $ERROR_CODE{'EX_USAGE'};
311      }
312      last;
313    }
314  }
315  close(DSPAM);
316}
317
318#
319# Check if source is known by DSPAM
320#
321if ($CONFIG{'SOURCE'} eq "") {
322  print STDERR "ERROR: Source can not be empty.\n" if ($CONFIG{'DEBUG'} == 1);
323  exit $ERROR_CODE{'EX_USAGE'};
324} elsif ($CONFIG{'SOURCE'} !~ /^(error|corpus|inoculation)$/) {
325  open(DSPAM, "$CONFIG{'DSPAM_BIN'} --help 2>&1|");
326  while(<DSPAM>) {
327    chomp;
328    if (/^.*\-\-source=\[([^\]]+).*$/) {
329      if ($CONFIG{'SOURCE'} !~ /^($1)$/) {
330        close(DSPAM);
331        print STDERR "ERROR: Source '" . $CONFIG{'SOURCE'} . "' is not known by DSPAM.\n" if ($CONFIG{'DEBUG'} == 1);
332        exit $ERROR_CODE{'EX_USAGE'};
333      }
334      last;
335    }
336  }
337  close(DSPAM);
338}
339
340#
341# Check if mode is known by DSPAM
342#
343if ($CONFIG{'MODE'} ne "") {
344  if ($CONFIG{'MODE'} !~ /^(toe|tum|teft|notrain|unlearn)$/) {
345    open(DSPAM, "$CONFIG{'DSPAM_BIN'} --help 2>&1|");
346    while(<DSPAM>) {
347      chomp;
348      if (/^.*\-\-mode=\[([^\]]+).*$/) {
349        if ($CONFIG{'MODE'} !~ /^($1)$/) {
350          close(DSPAM);
351          print STDERR "ERROR: Mode '" . $CONFIG{'MODE'} . "' is not known by DSPAM.\n" if ($CONFIG{'DEBUG'} == 1);
352          exit $ERROR_CODE{'EX_USAGE'};
353        }
354        last;
355      }
356    }
357    close(DSPAM);
358  }
359}
360
361#
362# Additional parameters to add when calling DSPAM
363#
364$CONFIG{'PARAMETERS'} = $CONFIG{'PARAMETERS'} . " --mode=" . quotemeta($CONFIG{'MODE'}) if ($CONFIG{'MODE'} ne "");
365
366#
367# Send complete message to DSPAM if source is inoculation or corups
368#
369if ($CONFIG{'SOURCE'} =~ /^(inoculation|corpus)$/) {
370  if ($CONFIG{'DEBUG'} == 1) {
371    print STDERR "DEBUG: Piping message to: $CONFIG{'DSPAM_BIN'} --source=" . quotemeta($CONFIG{'SOURCE'}) ." --class=" . quotemeta($CONFIG{'CLASS'}) . $CONFIG{'PARAMETERS'} . " --user " . quotemeta($CONFIG{'USER'}) . "\n";
372    exit $ERROR_CODE{'EX_OK'};
373  } else {
374    @MESSAGE = <>;
375    open(DSPAM, "|$CONFIG{'DSPAM_BIN'} --source=" . quotemeta($CONFIG{'SOURCE'}) ." --class=" . quotemeta($CONFIG{'CLASS'}) . $CONFIG{'PARAMETERS'} . " --user " . quotemeta($CONFIG{'USER'}));
376    print DSPAM @MESSAGE;
377    close(DSPAM);
378    exit $ERROR_CODE{'EX_OK'};
379  }
380}
381
382#
383# Extract DSPAM signature(s) and pass them to DSAPM
384#
385my $header = 1;
386my $sig_counter = 0;
387while (<>) {
388  chomp;
389  $header = 0 if (/^$/);
390  #last if ($header == 0 && $CONFIG{'HEADERS_ONLY'} == 1);
391  if (/^(X\-DSPAM\-Signature|[\t ]*!DSPAM):/i) {
392    if ($CONFIG{'HEADERS_ONLY'} == 1 || $CONFIG{'BODIES_ONLY'} == 0) {
393      if (/^X\-DSPAM\-Signature:[\t ](([0-9]+[,]{1})?([a-f0-9]+))[\t ]*$/i) {
394        if ($SIGNATURES{$1} != 1) {
395          $sig_counter++;
396          if ($sig_counter == 1 && $CONFIG{'SKIP_FIRST'} == 1) {
397            if ($CONFIG{'DEBUG'} == 1) {
398              print STDERR "DEBUG: Found DSPAM signature '" . $1 . "' in header";
399              print STDERR " (attachment)" if ($header == 0);
400              print STDERR ".\n";
401              print STDERR "DEBUG: By request skipping this DSPAM signature.\n";
402            }
403          } elsif ($CONFIG{'DEBUG'} == 1) {
404            print STDERR "DEBUG: Found DSPAM signature '" . $1 . "' in header";
405            print STDERR " (attachment)" if ($header == 0);
406            print STDERR ".\n";
407            print STDERR "DEBUG: $CONFIG{'DSPAM_BIN'} --source=" . quotemeta($CONFIG{'SOURCE'}) ." --class=" . quotemeta($CONFIG{'CLASS'}) . " --signature=" . quotemeta($1) . $CONFIG{'PARAMETERS'} . " --user " . quotemeta($CONFIG{'USER'}) . "\n";
408          } else {
409            system("$CONFIG{'DSPAM_BIN'} --source=" . quotemeta($CONFIG{'SOURCE'}) ." --class=" . quotemeta($CONFIG{'CLASS'}) . " --signature=" . quotemeta($1) . $CONFIG{'PARAMETERS'} . " --user " . quotemeta($CONFIG{'USER'}));
410          }
411          if ($sig_counter == 2 && $CONFIG{'SKIP_FIRST'} == 1 && $CONFIG{'FIRST_SIGNATURE_ONLY'} == 1) {
412            last;
413          } elsif ($sig_counter == 1 && $CONFIG{'SKIP_FIRST'} == 0 && $CONFIG{'FIRST_SIGNATURE_ONLY'} == 1) {
414            last;
415          }
416          $SIGNATURES{$1} = 1;
417        }
418      }
419    } elsif ($header == 0 && ($CONFIG{'HEADERS_ONLY'} == 0 || $CONFIG{'BODIES_ONLY'} == 1)) {
420      if (/^[\t ]*!DSPAM:(([0-9]+[,]{1})?([a-f0-9]+))![\t ]*$/i) {
421        if ($SIGNATURES{$1} != 1) {
422          $sig_counter++;
423          if ($sig_counter == 1 && $CONFIG{'SKIP_FIRST'} == 1) {
424            if ($CONFIG{'DEBUG'} == 1) {
425              print STDERR "DEBUG: Found DSPAM signature '" . $1 . "' in body.\n";
426              print STDERR "DEBUG: By request skipping this DSPAM signature.\n";
427            }
428          } elsif ($CONFIG{'DEBUG'} == 1) {
429            print STDERR "DEBUG: Found DSPAM signature '" . $1 . "' in body.\n";
430            print STDERR "DEBUG: $CONFIG{'DSPAM_BIN'} --source=" . quotemeta($CONFIG{'SOURCE'}) ." --class=" . quotemeta($CONFIG{'CLASS'}) . " --signature=" . quotemeta($1) . $CONFIG{'PARAMETERS'} . " --user " . quotemeta($CONFIG{'USER'}) . "\n";
431          } else {
432            system("$CONFIG{'DSPAM_BIN'} --source=" . quotemeta($CONFIG{'SOURCE'}) ." --class=" . quotemeta($CONFIG{'CLASS'}) . " --signature=" . quotemeta($1) . $CONFIG{'PARAMETERS'} . " --user " . quotemeta($CONFIG{'USER'}));
433          }
434          if ($sig_counter == 2 && $CONFIG{'SKIP_FIRST'} == 1 && $CONFIG{'FIRST_SIGNATURE_ONLY'} == 1) {
435            last;
436          } elsif ($sig_counter == 1 && $CONFIG{'SKIP_FIRST'} == 0 && $CONFIG{'FIRST_SIGNATURE_ONLY'} == 1) {
437            last;
438          }
439          $SIGNATURES{$1} = 1;
440        }
441      }
442    }
443  } elsif ($CONFIG{'DEBUG'} == 1) {
444    if (/(X\-DSPAM\-Signature|[\t ]*!DSPAM):[\t ]*(([0-9]+[,]{1})?([a-f0-9]+))!?.*/i) {
445      print STDERR "DEBUG: Found DSPAM signature '" . $2 . "' in message.\n";
446      print STDERR "DEBUG: The signature will NOT be used. The original line is:\n";
447      print STDERR "DEBUG: $_\n";
448    }
449  }
450}
451
452#
453# Exit
454#
455exit $ERROR_CODE{'EX_OK'};
Note: See TracBrowser for help on using the repository browser.