home *** CD-ROM | disk | FTP | other *** search
/ PC Open 100 / PC Open 100 CD 1.bin / CD1 / INTERNET / EMAIL / pop file / setup.exe / Proxy / Proxy.pm < prev   
Encoding:
Perl POD Document  |  2004-02-03  |  22.8 KB  |  703 lines

  1. package Proxy::Proxy;
  2.  
  3. # ---------------------------------------------------------------------------------------------
  4. #
  5. # This module implements the base class for all POPFile proxy Modules
  6. #
  7. # Copyright (c) 2001-2003 John Graham-Cumming
  8. #
  9. #   This file is part of POPFile
  10. #
  11. #   POPFile is free software; you can redistribute it and/or modify
  12. #   it under the terms of the GNU General Public License as published by
  13. #   the Free Software Foundation; either version 2 of the License, or
  14. #   (at your option) any later version.
  15. #
  16. #   POPFile is distributed in the hope that it will be useful,
  17. #   but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. #   GNU General Public License for more details.
  20. #
  21. #   You should have received a copy of the GNU General Public License
  22. #   along with POPFile; if not, write to the Free Software
  23. #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  24. #
  25. #   Modified by     Sam Schinke (sschinke@users.sourceforge.net)
  26. #
  27. # ---------------------------------------------------------------------------------------------
  28.  
  29. use POPFile::Module;
  30. @ISA = ( "POPFile::Module" );
  31.  
  32. use IO::Handle;
  33. use IO::Socket;
  34. use IO::Select;
  35.  
  36. # A handy variable containing the value of an EOL for networks
  37. my $eol = "\015\012";
  38.  
  39. use POSIX ":sys_wait_h";
  40.  
  41. #----------------------------------------------------------------------------
  42. # new
  43. #
  44. #   Class new() function, all real work gets done by initialize and
  45. #   the things set up here are more for documentation purposes than
  46. #   anything so that you know that they exists
  47. #
  48. #----------------------------------------------------------------------------
  49. sub new
  50. {
  51.     my $type = shift;
  52.     my $self = POPFile::Module->new();
  53.  
  54.     # A reference to the classifier
  55.  
  56.     $self->{classifier__}     = 0;
  57.  
  58.     # List of file handles to read from active children, this
  59.     # maps the PID for each child to its associated pipe handle
  60.  
  61.     $self->{children__}        = {};
  62.  
  63.     # Reference to a child() method called to handle a proxy
  64.     # connection, reference to flush_child_data() method used
  65.     # to clear out pipes
  66.  
  67.     $self->{child_}            = 0;
  68.     $self->{flush_child_data_} = \&flush_child_data_;
  69.  
  70.     # Holding variable for MSWin32 pipe handling
  71.  
  72.     $self->{pipe_cache__};
  73.  
  74.     # This is where we keep the session with the Classifier::Bayes
  75.     # module
  76.  
  77.     $self->{api_session__} = '';
  78.  
  79.     # This is the error message returned if the connection at any
  80.     # time times out while handling a command
  81.     #
  82.     # $self->{connection_timeout_error_} = '';
  83.  
  84.     # This is the error returned (with the host and port appended)
  85.     # if contacting the remote server fails
  86.     #
  87.     # $self->{connection_failed_error_}  = '';
  88.  
  89.     # This is a regular expression used by get_response_ to determine
  90.     # if a response from the remote server is good or not (good being
  91.     # that the last command succeeded)
  92.     #
  93.     # $self->{good_response_}            = '';
  94.  
  95.     return bless $self, $type;
  96. }
  97.  
  98. # ---------------------------------------------------------------------------------------------
  99. #
  100. # initialize
  101. #
  102. # Called to initialize the Proxy, most of this is handled by a subclass of this
  103. # but here we set the 'enabled' flag
  104. #
  105. # ---------------------------------------------------------------------------------------------
  106. sub initialize
  107. {
  108.     my ( $self ) = @_;
  109.  
  110.     $self->config_( 'enabled', 1 );
  111.  
  112.     return 1;
  113. }
  114.  
  115. # ---------------------------------------------------------------------------------------------
  116. #
  117. # start
  118. #
  119. # Called when all configuration information has been loaded from disk.
  120. #
  121. # The method should return 1 to indicate that it started correctly, if it returns
  122. # 0 then POPFile will abort loading immediately
  123. #
  124. # ---------------------------------------------------------------------------------------------
  125. sub start
  126. {
  127.     my ( $self ) = @_;
  128.  
  129.     # Open the socket used to receive request for proxy service
  130.  
  131.     $self->{server__} = IO::Socket::INET->new( Proto     => 'tcp', # PROFILE BLOCK START
  132.                                     $self->config_( 'local' ) == 1 ? (LocalAddr => 'localhost') : (),
  133.                                     LocalPort => $self->config_( 'port' ),
  134.                                     Listen    => SOMAXCONN,
  135.                                     Reuse     => 1 ); # PROFILE BLOCK STOP
  136.  
  137.     if ( !defined( $self->{server__} ) ) {
  138.         my $port = $self->config_( 'port' );
  139.         my $name = $self->name();
  140.         print STDERR <<EOM; # PROFILE BLOCK START
  141.  
  142. \nCouldn't start the $name proxy because POPFile could not bind to the
  143. listen port $port. This could be because there is another service
  144. using that port or because you do not have the right privileges on
  145. your system (On Unix systems this can happen if you are not root
  146. and the port you specified is less than 1024).
  147.  
  148. EOM
  149. # PROFILE BLOCK STOP
  150.         return 0;
  151.     }
  152.  
  153.     # This is used to perform select calls on the $server socket so that we can decide when there is
  154.     # a call waiting an accept it without having to block
  155.  
  156.     $self->{selector__} = new IO::Select( $self->{server__} );
  157.  
  158.     return 1;
  159. }
  160.  
  161. # ---------------------------------------------------------------------------------------------
  162. #
  163. # stop
  164. #
  165. # Called when POPFile is closing down, this is the last method that will get called before
  166. # the object is destroyed.  There is not return value from stop().
  167. #
  168. # ---------------------------------------------------------------------------------------------
  169. sub stop
  170. {
  171.     my ( $self ) = @_;
  172.  
  173.     if ( $self->{api_session__} ne '' ) {
  174.         $self->{classifier__}->release_session_key( $self->{api_session__} );
  175.     }
  176.  
  177.     # Need to close all the duplicated file handles, this include the POP3 listener
  178.     # and all the reading ends of pipes to active children
  179.  
  180.     close $self->{server__} if ( defined( $self->{server__} ) );
  181.  
  182.     for my $kid (keys %{$self->{children__}}) {
  183.         close $self->{children__}{$kid};
  184.         delete $self->{children__}{$kid};
  185.     }
  186. }
  187.  
  188. # ---------------------------------------------------------------------------------------------
  189. #
  190. # reaper
  191. #
  192. # Called when a child process terminates somewhere in POPFile.  The object should check
  193. # to see if it was one of its children and do any necessary processing by calling waitpid()
  194. # on any child handles it has
  195. #
  196. # There is no return value from this method
  197. #
  198. # ---------------------------------------------------------------------------------------------
  199. sub reaper
  200. {
  201.     my ( $self ) = @_;
  202.  
  203.     # Look for children that have completed and then flush the data from their
  204.     # associated pipe and see if any of our children have data ready to read from their pipes,
  205.  
  206.     my @kids = keys %{$self->{children__}};
  207.  
  208.     if ( $#kids >= 0 ) {
  209.         for my $kid (@kids) {
  210.             if ( waitpid( $kid, &WNOHANG ) == $kid ) {
  211.                 $self->{flush_child_data_}( $self, $self->{children__}{$kid} );
  212.                 close $self->{children__}{$kid};
  213.                 delete $self->{children__}{$kid};
  214.  
  215.                 $self->log_( "Done with $kid (" . scalar(keys %{$self->{children__}}) . " to go)" );
  216.             }
  217.         }
  218.     }
  219. }
  220.  
  221. # ---------------------------------------------------------------------------------------------
  222. #
  223. # read_pipe_
  224. #
  225. # reads a single message from a pipe in a cross-platform way.
  226. # returns undef if the pipe has no message
  227. #
  228. # $handle   The handle of the pipe to read
  229. #
  230. # ---------------------------------------------------------------------------------------------
  231. sub read_pipe_
  232. {
  233.     my ($self, $handle) = @_;
  234.  
  235.     if ( $^O eq "MSWin32" ) {
  236.  
  237.         # bypasses bug in -s $pipe under ActivePerl
  238.  
  239.         my $message;         # PROFILE PLATFORM START MSWin32
  240.  
  241.         if ( &{ $self->{pipeready_} }($handle) ) {
  242.  
  243.             # add data to the pipe cache whenever the pipe is ready
  244.  
  245.             sysread($handle, my $string, -s $handle);
  246.  
  247.             # push messages onto the end of our cache
  248.  
  249.             $self->{pipe_cache__} .= $string;
  250.         }
  251.  
  252.         # pop the oldest message;
  253.  
  254.         $message = $1 if ($self->{pipe_cache__} =~ s/(.*?\n)//);
  255.  
  256.         return $message;        # PROFILE PLATFORM STOP
  257.     } else {
  258.  
  259.         # do things normally
  260.  
  261.         if ( &{ $self->{pipeready_} }($handle) ) {
  262.             return <$handle>;
  263.         }
  264.     }
  265.  
  266.     return undef;
  267. }
  268.  
  269. # ---------------------------------------------------------------------------------------------
  270. #
  271. # flush_child_data_
  272. #
  273. # Called to flush data from the pipe of each child as we go, I did this because there
  274. # appears to be a problem on Windows where the pipe gets a lot of read data in it and
  275. # then causes the child not to be terminated even though we are done.  Also this is nice
  276. # because we deal with the statistics as we go
  277. #
  278. # $handle   The handle of the child's pipe
  279. #
  280. # ---------------------------------------------------------------------------------------------
  281. sub flush_child_data_
  282. {
  283.     my ( $self, $handle ) = @_;
  284.  
  285.     my $stats_changed = 0;
  286.     my $message;
  287.  
  288.     while ( ($message = $self->read_pipe_( $handle )) && defined($message) )
  289.     {
  290.         $message =~ s/[\r\n]//g;
  291.  
  292.         $self->log_( "Child proxy message $message" );
  293.  
  294.         if ( $message =~ /CLASS:([^ ]*) ([^ ]*)/ ) {
  295.             $self->{classifier__}->classified( $2, $1 );
  296.         }
  297.  
  298.         if ( $message =~ /NEWFL:(.*)/ ) {
  299.             $self->mq_post_( 'NEWFL', $1, '' );
  300.         }
  301.  
  302.         if ( $message =~ /LOGIN:(.*)/ ) {
  303.             $self->mq_post_( 'LOGIN', $1, '' );
  304.         }
  305.     }
  306. }
  307.  
  308. # ---------------------------------------------------------------------------------------------
  309. #
  310. # service
  311. #
  312. # service() is a called periodically to give the module a chance to do housekeeping work.
  313. #
  314. # If any problem occurs that requires POPFile to shutdown service() should return 0 and
  315. # the top level process will gracefully terminate POPFile including calling all stop()
  316. # methods.  In normal operation return 1.
  317. #
  318. # ---------------------------------------------------------------------------------------------
  319. sub service
  320. {
  321.     my ( $self ) = @_;
  322.  
  323.     # See if any of the children have passed up statistics data through their
  324.     # pipes and deal with it now
  325.  
  326.     for my $kid (keys %{$self->{children__}}) {
  327.         $self->{flush_child_data_}( $self, $self->{children__}{$kid} );
  328.     }
  329.  
  330.     # Accept a connection from a client trying to use us as the mail server.  We service one client at a time
  331.     # and all others get queued up to be dealt with later.  We check the alive boolean here to make sure we
  332.     # are still allowed to operate. See if there's a connection waiting on the $server by getting the list of
  333.     # handles with data to read, if the handle is the server then we're off.
  334.  
  335.     if ( ( defined( $self->{selector__}->can_read(0) ) ) && ( $self->{alive_} ) ) {
  336.         if ( my $client = $self->{server__}->accept() ) {
  337.  
  338.             # Check to see if we have obtained a session key yet
  339.  
  340.             if ( $self->{api_session__} eq '' ) {
  341.                 $self->{api_session__} = $self->{classifier__}->get_session_key( 'admin', '' );
  342.         }
  343.  
  344.             # Check that this is a connection from the local machine, if it's not then we drop it immediately
  345.             # without any further processing.  We don't want to act as a proxy for just anyone's email
  346.  
  347.             my ( $remote_port, $remote_host ) = sockaddr_in( $client->peername() );
  348.  
  349.             if  ( ( $self->config_( 'local' ) == 0 ) || ( $remote_host eq inet_aton( "127.0.0.1" ) ) ) {
  350.  
  351.                 # Now that we have a good connection to the client fork a subprocess to handle the communication
  352.                 # and set the socket to binmode so that no CRLF translation goes on
  353.  
  354.                 $self->global_config_( 'download_count', $self->global_config_( 'download_count' ) + 1 );
  355.  
  356.                 # If we have force_fork turned on then we will do a fork, otherwise we will handle this
  357.                 # inline, in the inline case we need to create the two ends of a pipe that will be used
  358.                 # as if there was a child process
  359.  
  360.                 binmode( $client );
  361.  
  362.                 if ( $self->config_( 'force_fork' ) ) {
  363.                     my ( $pid, $pipe ) = &{$self->{forker_}};
  364.  
  365.                     # If we are in the parent process then push the pipe handle onto the children list
  366.  
  367.                     if ( ( defined( $pid ) ) && ( $pid != 0 ) ) {
  368.                         $self->{children__}{$pid} = $pipe;
  369.                     }
  370.  
  371.                     # If we fail to fork, or are in the child process then process this request
  372.  
  373.                     if ( !defined( $pid ) || ( $pid == 0 ) ) {
  374.                         $self->{child_}( $self, $client, $self->global_config_( 'download_count' ), $pipe, 0, $pid, $self->{api_session__} );
  375.                         $self->{flush_child_data_}( $self, $pipe );
  376.                         exit(0) if ( defined( $pid ) );
  377.                     }
  378.             } else {
  379.                     pipe my $reader, my $writer;
  380.  
  381.                     $self->{child_}( $self, $client, $self->global_config_( 'download_count' ), $writer, $reader, $$, $self->{api_session__} );
  382.                     $self->{flush_child_data_}( $self, $reader );
  383.                     close $reader;
  384.                 }
  385.             }
  386.  
  387.             close $client;
  388.         }
  389.     }
  390.  
  391.     return 1;
  392. }
  393.  
  394.  
  395. # ---------------------------------------------------------------------------------------------
  396. #
  397. # yield_
  398. #
  399. # Called by a proxy child process to allow the parent to do work, this only does anything
  400. # in the case where we didn't fork for the child process
  401. #
  402. # ---------------------------------------------------------------------------------------------
  403. sub yield_
  404. {
  405.     my ( $self, $pipe, $pid ) = @_;
  406.  
  407.     if ( $pid != 0 ) {
  408.         $self->{flush_child_data_}( $self, $pipe )
  409.     }
  410. }
  411.  
  412. # ---------------------------------------------------------------------------------------------
  413. #
  414. # forked
  415. #
  416. # This is called when some module forks POPFile and is within the context of the child
  417. # process so that this module can close any duplicated file handles that are not needed.
  418. #
  419. # There is no return value from this method
  420. #
  421. # ---------------------------------------------------------------------------------------------
  422. sub forked
  423. {
  424.     my ( $self ) = @_;
  425.  
  426.     close $self->{server__};
  427.  
  428.     for my $kid (keys %{$self->{children__}}) {
  429.         close $self->{children__}{$kid};
  430.         delete $self->{children__}{$kid};
  431.     }
  432. }
  433.  
  434. # ---------------------------------------------------------------------------------------------
  435. #
  436. # tee_
  437. #
  438. # $socket   The stream (created with IO::) to send the string to
  439. # $text     The text to output
  440. #
  441. # Sends $text to $socket and sends $text to debug output
  442. #
  443. # ---------------------------------------------------------------------------------------------
  444. sub tee_
  445. {
  446.     my ( $self, $socket, $text ) = @_;
  447.  
  448.     # Send the message to the debug output and then send it to the appropriate socket
  449.     $self->log_( $text );
  450.     print $socket $text;
  451. }
  452.  
  453. # ---------------------------------------------------------------------------------------------
  454. #
  455. # echo_to_regexp_
  456. #
  457. # $mail     The stream (created with IO::) to send the message to (the remote mail server)
  458. # $client   The local mail client (created with IO::) that needs the response
  459. # $regexp   The pattern match to terminate echoing, compile using qr/pattern/
  460. # $log      (OPTIONAL) log output if 1, defaults to 0 if unset
  461. # $suppress (OPTIONAL) suppress any lines that match, compile using qr/pattern/
  462. #
  463. # echo all information from the $mail server until a single line matching $regexp is seen
  464. #
  465. # ---------------------------------------------------------------------------------------------
  466. sub echo_to_regexp_
  467. {
  468.     my ( $self, $mail, $client, $regexp, $log, $suppress ) = @_;
  469.  
  470.     $log = 0 if (!defined($log));
  471.  
  472.     while ( my $line = $self->slurp_( $mail ) ) {
  473.         if (!defined($suppress) || !( $line =~ $suppress )) {
  474.             if ( !$log ) {
  475.                 print $client $line;
  476.             } else {
  477.                 $self->tee_( $client, $line );
  478.             }
  479.         } else {
  480.             $self->log_("Suppressed: $line");
  481.         }
  482.  
  483.     if ( $line =~ $regexp ) {
  484.             last;
  485.     }
  486.     }
  487. }
  488.  
  489. # ---------------------------------------------------------------------------------------------
  490. #
  491. # echo_to_dot_
  492. #
  493. # $mail     The stream (created with IO::) to send the message to (the remote mail server)
  494. # $client   The local mail client (created with IO::) that needs the response
  495. #
  496. # echo all information from the $mail server until a single line with a . is seen
  497. #
  498. # ---------------------------------------------------------------------------------------------
  499. sub echo_to_dot_
  500. {
  501.     my ( $self, $mail, $client ) = @_;
  502.  
  503.     # The termination has to be a single line with exactly a dot on it and nothing
  504.     # else other than line termination characters.  This is vital so that we do
  505.     # not mistake a line beginning with . as the end of the block
  506.  
  507.     $self->echo_to_regexp_( $mail, $client, qr/^\.(\r\n|\r|\n)$/);
  508. }
  509.  
  510. # ---------------------------------------------------------------------------------------------
  511. #
  512. # get_response_
  513. #
  514. # $mail     The stream (created with IO::) to send the message to (the remote mail server)
  515. # $client   The local mail client (created with IO::) that needs the response
  516. # $command  The text of the command to send (we add an EOL)
  517. # $null_resp Allow a null response
  518. # $suppress If set to 1 then the response does not go to the client
  519. #
  520. # Send $command to $mail, receives the response and echoes it to the $client and the debug
  521. # output.  Returns the response and a failure code indicating false if there was a timeout
  522. #
  523. # ---------------------------------------------------------------------------------------------
  524. sub get_response_
  525. {
  526.     my ( $self, $mail, $client, $command, $null_resp, $suppress ) = @_;
  527.  
  528.     $null_resp = 0 if (!defined $null_resp);
  529.     $suppress  = 0 if (!defined $suppress);
  530.  
  531.     unless ( defined($mail) && $mail->connected ) {
  532.        # $mail is undefined - return an error intead of crashing
  533.        $self->tee_(  $client, "$self->{connection_timeout_error_}$eol" );
  534.        return ( $self->{connection_timeout_error_}, 0 );
  535.     }
  536.  
  537.     # Send the command (followed by the appropriate EOL) to the mail server
  538.     $self->tee_( $mail, $command. $eol );
  539.  
  540.     my $response;
  541.  
  542.     # Retrieve a single string containing the response
  543.  
  544.     my $selector = new IO::Select( $mail );
  545.     my ($ready) = $selector->can_read( (!$null_resp?$self->global_config_( 'timeout' ):.5) );
  546.  
  547.     if ( ( defined( $ready ) ) && ( $ready == $mail ) ) {
  548.         $response = $self->slurp_( $mail );
  549.  
  550.         if ( $response ) {
  551.  
  552.             # Echo the response up to the mail client
  553.  
  554.             $self->tee_( $client, $response ) if ( !$suppress );
  555.             return ( $response, 1 );
  556.         }
  557.     }
  558.  
  559.     if ( !$null_resp ) {
  560.         # An error has occurred reading from the mail server
  561.  
  562.         $self->tee_(  $client, "$self->{connection_timeout_error_}$eol" );
  563.         return ( $self->{connection_timeout_error_}, 0 );
  564.     } else {
  565.         $self->tee_($client, "");
  566.         return ( "", 1 );
  567.     }
  568. }
  569.  
  570. # ---------------------------------------------------------------------------------------------
  571. #
  572. # echo_response_
  573. #
  574. # $mail     The stream (created with IO::) to send the message to (the remote mail server)
  575. # $client   The local mail client (created with IO::) that needs the response
  576. # $command  The text of the command to send (we add an EOL)
  577. # $suppress If set to 1 then the response does not go to the client
  578. #
  579. # Send $command to $mail, receives the response and echoes it to the $client and the debug
  580. # output.
  581. #
  582. # Returns one of three values
  583. #
  584. # 0 Successfully sent the command and got a positive response
  585. # 1 Sent the command and got a negative response
  586. # 2 Failed to send the command (e.g. a timeout occurred)
  587. #
  588. # ---------------------------------------------------------------------------------------------
  589. sub echo_response_
  590. {
  591.     my ( $self, $mail, $client, $command, $suppress ) = @_;
  592.  
  593.     # Determine whether the response began with the string +OK.  If it did then return 1
  594.     # else return 0
  595.  
  596.     my ( $response, $ok ) = $self->get_response_( $mail, $client, $command, 0, $suppress );
  597.  
  598.     if ( $ok == 1 ) {
  599.         if ( $response =~ /$self->{good_response_}/ ) {
  600.             return 0;
  601.     } else {
  602.             return 1;
  603.         }
  604.     } else {
  605.         return 2;
  606.     }
  607. }
  608.  
  609. # ---------------------------------------------------------------------------------------------
  610. #
  611. # verify_connected_
  612. #
  613. # $mail        The handle of the real mail server
  614. # $client      The handle to the mail client
  615. # $hostname    The host name of the remote server
  616. # $port        The port
  617. #
  618. # Check that we are connected to $hostname on port $port putting the open handle in $mail.
  619. # Any messages need to be sent to $client
  620. #
  621. # ---------------------------------------------------------------------------------------------
  622. sub verify_connected_
  623. {
  624.     my ( $self, $mail, $client, $hostname, $port ) = @_;
  625.  
  626.     # Check to see if we are already connected
  627.     return $mail if ( $mail && $mail->connected );
  628.  
  629.     # Connect to the real mail server on the standard port
  630.    $mail = IO::Socket::INET->new( # PROFILE BLOCK START
  631.                 Proto    => "tcp",
  632.                 PeerAddr => $hostname,
  633.                 PeerPort => $port ); # PROFILE BLOCK STOP
  634.  
  635.     # Check that the connect succeeded for the remote server
  636.     if ( $mail ) {
  637.         if ( $mail->connected )  {
  638.  
  639.             $self->log_( "Connected to $hostname:$port timeout " . $self->global_config_( 'timeout' ) );
  640.  
  641.             # Set binmode on the socket so that no translation of CRLF
  642.             # occurs
  643.  
  644.             binmode( $mail );
  645.  
  646.             # Wait 10 seconds for a response from the remote server and if
  647.             # there isn't one then give up trying to connect
  648.  
  649.             my $selector = new IO::Select( $mail );
  650.             last unless () = $selector->can_read($self->global_config_( 'timeout' ));
  651.  
  652.             # Read the response from the real server and say OK
  653.  
  654.             my $buf        = '';
  655.             my $max_length = 8192;
  656.             my $n          = sysread( $mail, $buf, $max_length, length $buf );
  657.  
  658.             if ( !( $buf =~ /[\r\n]/ ) ) {
  659.                 my $hit_newline = 0;
  660.                 my $temp_buf;
  661.  
  662.                 # Read until timeout or a newline (newline _should_ be immediate)
  663.  
  664.                 for my $i ( 0..($self->global_config_( 'timeout' ) * 100) ) {
  665.                     if ( !$hit_newline ) {
  666.                         $temp_buf = $self->flush_extra_( $mail, $client, 1 );
  667.                         $hit_newline = ( $temp_buf =~ /[\r\n]/ );
  668.                         $buf .= $temp_buf;
  669.                     } else {
  670.                         last;
  671.                     }
  672.                 }
  673.             }
  674.  
  675.             $self->log_( "Connection returned: $buf" );
  676.  
  677.             # Clean up junk following a newline
  678.  
  679.             for my $i ( 0..4 ) {
  680.                 $self->flush_extra_( $mail, $client, 1 );
  681.             }
  682.  
  683.             return $mail;
  684.         }
  685.     }
  686.  
  687.     # Tell the client we failed
  688.     $self->tee_(  $client, "$self->{connection_failed_error_} $hostname:$port$eol" );
  689.  
  690.     return undef;
  691. }
  692.  
  693. # SETTER
  694.  
  695. sub classifier
  696. {
  697.     my ( $self, $classifier ) = @_;
  698.  
  699.     $self->{classifier__} = $classifier;
  700. }
  701.  
  702. 1;
  703.