Matt Sergeant wrote:
> 
> I'm thinking more on the lines of pushing back reads if they're not full 
> lines.
> 
> Probably needs a bit of support for that in lib/Danga/Client.pm

Attached is an attempt to do that. The reason for this is that I've seen
another case of end of data marker coming in two different packets and
the previous fix does not work: one packet ends with CRLF. , the
following packet starts with CRLF. A test script is attached too.

This new attempt reverts all previous changes to Qpsmtpd::PollServer and
changes Danga::Client to send to the callback full lines: either the
entire packet if it ends with CRLF or the part up to the last CRLF,
inclusive. What is left, a possible partial line, is buffered and
processed when the next packet comes in.


Thanks,
Radu Greab

=== lib/Qpsmtpd/PollServer.pm
==================================================================
--- lib/Qpsmtpd/PollServer.pm   (revision 154)
+++ lib/Qpsmtpd/PollServer.pm   (revision 155)
@@ -16,7 +16,6 @@
     start_time
     cmd_timeout
     conn
-    prev_crlf
     _auth
     _auth_mechanism
     _auth_state
@@ -213,7 +212,6 @@
     $self->{header_lines} = '';
     $self->{data_size} = 0;
     $self->{in_header} = 1;
-    $self->{prev_crlf} = 0;
     $self->{max_size} = ($self->config('databytes'))[0] || 0;
     
     $self->log(LOGDEBUG, "max_size: $self->{max_size} / size: 
$self->{data_size}");
@@ -231,18 +229,13 @@
 
     my $done = 0;
     my $remainder;
-    if ($data =~ s/\r\n\.\r\n(.*)\z/\r\n/ms
-        ||
-        ($self->{prev_crlf} && $data =~ s/^\.\r\n(.*)\z//ms)
-       ) 
-    {
+    if ($data =~ s/^\.\r\n(.*)\z//ms) {
         $remainder = $1;
         $done = 1;
     }
 
     # add a transaction->blocked check back here when we have line by line 
plugin access...
     unless (($self->{max_size} and $self->{data_size} > $self->{max_size})) {
-        $self->{prev_crlf} = $data =~ /\r\n\z/;
         $data =~ s/\r\n/\n/mg;
         $data =~ s/^\.\./\./mg;
         
=== lib/Danga/Client.pm
==================================================================
--- lib/Danga/Client.pm (revision 154)
+++ lib/Danga/Client.pm (revision 155)
@@ -54,6 +54,26 @@
     $self->{callback} = $callback;
 }
 
+sub process_chunk {
+    my Danga::Client $self = shift;
+    my $callback = shift;
+
+    my $last_crlf = rindex($self->{line}, "\r\n");
+
+    if ($last_crlf != -1) {
+        if ($last_crlf + 2 == length($self->{line})) {
+            my $data = $self->{line};
+            $self->{line} = '';
+            $callback->($data);
+        }
+        else {
+            my $data = substr($self->{line}, 0, $last_crlf);
+            $self->{line} = substr($self->{line}, $last_crlf + 1);
+            $callback->($data);
+        }
+    }
+}
+
 sub get_chunks {
     my Danga::Client $self = shift;
     my ($bytes, $callback) = @_;
@@ -61,8 +81,7 @@
         die "get_bytes/get_chunks currently in progress!";
     }
     $self->{read_bytes} = $bytes;
-    $callback->($self->{line}) if length($self->{line});
-    $self->{line} = '';
+    $self->process_chunk($callback) if length($self->{line});
     $self->{callback} = $callback;
     $self->{get_chunks} = 1;
 }
@@ -84,7 +103,8 @@
         if ($self->{get_chunks}) {
             my $bref = $self->read($self->{read_bytes});
             return $self->close($!) unless defined $bref;
-            $self->{callback}->($$bref) if length($$bref);
+            $self->{line} .= $$bref;
+            $self->process_chunk($self->{callback}) if length($self->{line});
             return;
         }
         if ($self->{read_bytes} > 0) {
#!/usr/bin/perl -w

use strict;
use Net::SMTP;

my $smtp = Net::SMTP->new('localhost:25', Debug => 1);
$smtp->mail('');
$smtp->to('[EMAIL PROTECTED]');
$smtp->data;

$smtp->rawdatasend("Subject: test\r\n\r\nOne chunk\r\n.");

sleep 1;  # let qpsmtpd get the previous data as a packet

$smtp->rawdatasend("\r\n");

print "server responds: ", $smtp->response, "\n";

$smtp->rawdatasend("QUIT\r\n");

Reply via email to