Branch: Tag:

2003-10-16

2003-10-16 13:01:35 by David Gourdelier <vida@caudium.net>

. Bugfix for the LMTP protocol: The RFC specifies that we must reply to
each "rcpt to" commands after the DATA command.
. More debug informations
. Updated doc for the LMTP protocol

Rev: lib/modules/Protocols.pmod/LMTP.pmod:1.3
Rev: lib/modules/Protocols.pmod/SMTP.pmod:1.26

230:    // The commands this module supports    array(string) commands = ({ "ehlo", "helo", "mail", "rcpt", "data",    "rset", "vrfy", "quit", "noop" }); +  // do we speak LMTP ? +  int lmtp_mode = 0;       // the fd of the socket    static object fd = Stdio.File();
253:       // the sequence of commands the client send    static array(string) sequence = ({ }); +  // the message id of the current mail +  private string|int messageid;       // the callback functions used to guess if user is ok or not    static function cb_rcptto;
264:       array(string) features = ({ "PIPELINING", "8BITMIME", "SIZE " + maxsize });    -  static void handle_timeout() +  static void handle_timeout(string cmd)    { -  catch(fd->write("421 Error: timeout exceeded\r\n")); -  close_cb(); +  string errmsg = "421 Error: timeout exceeded after command " + +  cmd || "unknown command!" + "\r\n"; +  catch(fd->write(errmsg)); +  log(errmsg); +  close_cb(1);    }       static void outcode(int code)
280:       static void log(string fmt, mixed ... args)    { -  werror("Pike SMTP server : " + fmt + "\n", args); +  string errmsg = Calendar.now()->format_time() + +  " Pike SMTP server : "; +  if(messageid) +  errmsg += messageid + ": "; +  errmsg += fmt + "\n"; +  werror(errmsg, args);    }       // make the received header -  static string received(int messageid) +  static string received()    {    string remotehost =    Protocols.DNS.client()->gethostbyaddr(remoteaddr)[0]    || remoteaddr;    string rec; -  +  string mode = lmtp_mode ? "LMTP": "ESMTP";    rec=sprintf("from %s (%s [%s]) " -  "by %s (Pike SMTP server) with %s id %d ; %s", +  "by %s (Pike %s server) with %s id %d ; %s",    ident, remotehost, remoteaddr, -  gethostname(), "ESMTP", messageid, +  gethostname(), mode, mode, messageid,    Calendar.now()->format_smtp());    return rec;    }
301:    void helo(string argument)    {    remove_call_out(handle_timeout); -  call_out(handle_timeout, 310); +  call_out(handle_timeout, 310, "HELO");    if(sizeof(argument) > 0)    {    fd->write("250 %s\r\n", localhost);
318:    void ehlo(string argument)    {    remove_call_out(handle_timeout); -  call_out(handle_timeout, 310); +  call_out(handle_timeout, 310, "EHLO");    if(sizeof(argument) > 0)    {    string out = "250-" + localhost + "\r\n";
388:    void mail(string argument)    {    remove_call_out(handle_timeout); -  call_out(handle_timeout, 310); +  call_out(handle_timeout, 310, "MAIL FROM");    int sequence_ok = 0;    foreach(({ "ehlo", "helo", "lhlo" }), string needle)    {
443:    {    mixed err;    remove_call_out(handle_timeout); -  call_out(handle_timeout, 310); +  call_out(handle_timeout, 310, "RCPT TO");    if(!has_value(sequence, "mail from"))    {    outcode(503);
479:    void data(string argument)    {    remove_call_out(handle_timeout); -  call_out(handle_timeout, 610); +  call_out(handle_timeout, 610, "DATA");    if(!has_value(sequence, "rcpt to"))    {    outcode(503);
491:       MIME.Message format_headers(MIME.Message message)    { -  int messageid = hash(message->getdata()[..1000]) || random(100000); +  messageid = hash(message->getdata()[..1000]) || random(100000);    // first add missing headers    if(!message->headers->to)    message->headers->to = "Undisclosed-recipients";
500:    if(!message->headers->subject)    message->headers->subject = "";    if(!message->headers->received) -  message->headers->received = received(messageid); +  message->headers->received = received();    else -  message->headers->received = received(messageid) +  message->headers->received = received()    + "\0"+message->headers->received;    if(!message->headers["message-id"])    {
538:    log(describe_backtrace(err));    return;    } +  // if we are in LMTP mode we call cb_data for each recipient +  // and with one recipient. This way we have one mime message per +  // recipient and one outcode to display to the client per recipient +  // (that is LMTP specific) +  if(lmtp_mode) +  { +  foreach(mailto, string recipient) +  {    int check;    if(givedata) -  +  err = catch(check = cb_data(copy_value(message), mailfrom, +  recipient, content)); +  else +  err = catch(check = cb_data(copy_value(message), mailfrom, recipient)); +  if(err || !check) +  { +  outcode(554); +  log(describe_backtrace(err)); +  continue; +  } +  outcode(check); +  } +  } +  // SMTP mode, cb_data is called one time with an array of recipients +  // and the same MIME object +  else +  { +  int check; +  if(givedata)    err = catch(check = cb_data(message, mailfrom, mailto, content));    else    err = catch(check = cb_data(message, mailfrom, mailto));
551:    }    outcode(check);    } +  }       void noop()    {    remove_call_out(handle_timeout); -  call_out(handle_timeout, 310); +  call_out(handle_timeout, 310, "NOOP");    outcode(250);    }       void rset()    {    remove_call_out(handle_timeout); -  call_out(handle_timeout, 310); +  call_out(handle_timeout, 310, "RSET");    inputbuffer = "";    mailfrom = "";    mailto = ({ }); -  +  messageid = 0;    //sequence = ({ });    outcode(250);    }
573:    void vrfy()    {    remove_call_out(handle_timeout); -  call_out(handle_timeout, 310); +  call_out(handle_timeout, 310, "VRFY");    outcode(252);    }   
581:    {    fd->write("221 " + replace(replycodes[221], "<host>", localhost)    + "\r\n"); -  close_cb(); +  close_cb(1);    }       static int launch_functions(string line)
673:    fd->set_write_callback(0);    }    -  static void close_cb() +  static void close_cb(int i_close_the_stream)    { -  +  if(!i_close_the_stream) +  { +  string errmsg = "Connexion closed by client "; +  if(sequence && sizeof(sequence) > 1) +  errmsg += sequence[-1]; +  log(errmsg); +  }    catch (fd->close());    remove_call_out(handle_timeout);    }
695:    {    fd->write("421 " + replace(replycodes[421], "<host>", localhost)    + "\r\n"); -  close_cb(); +  close_cb(1);    return;    }    if(!localaddr)    {    fd->write("421 " + replace(replycodes[421], "<host>", localhost)    + "\r\n"); -  close_cb(); +  close_cb(1);    return;    }    //log("connection from %s to %s:%d", remoteaddr, localaddr, localport);    fd->set_nonblocking(read_cb, write_cb, close_cb); -  call_out(handle_timeout, 300); +  call_out(handle_timeout, 300, "'First connexion'");    }      };