1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
  
15
  
16
  
17
  
18
  
19
  
20
  
21
  
22
  
23
  
24
  
25
  
26
  
27
  
28
  
29
  
30
  
31
  
32
  
33
  
34
  
35
  
36
  
37
  
38
  
39
  
40
  
41
  
42
  
43
  
44
  
45
  
46
  
47
  
48
  
49
  
50
  
51
  
52
  
53
  
54
  
55
  
56
  
57
  
58
  
59
  
60
  
61
  
62
  
63
  
64
  
65
  
66
  
67
  
68
  
69
  
70
  
71
  
72
  
73
  
74
  
75
  
76
  
77
  
78
  
79
  
80
  
81
  
82
  
83
  
84
  
85
  
86
  
87
  
88
  
89
  
90
  
91
  
92
  
93
  
94
  
95
  
96
  
97
  
98
  
99
  
100
  
101
  
102
  
103
  
104
  
105
  
106
  
107
  
108
  
109
  
110
  
111
  
112
  
113
  
114
  
115
  
116
  
117
  
118
  
119
  
120
  
121
  
122
  
123
  
124
  
125
  
126
  
127
  
128
  
129
  
130
  
131
  
132
  
133
  
134
  
135
  
136
  
137
  
138
  
139
  
140
  
141
  
142
  
143
  
144
  
145
  
146
  
147
  
148
  
149
  
150
  
151
  
152
  
153
  
154
  
155
  
156
  
157
  
158
  
159
  
#pike __REAL_VERSION__ 
class Configuration { 
  inherit .SMTP.Configuration; 
} 
 
class Connection { 
  inherit .SMTP.Connection; 
  // The commands this module supports 
  mapping(string:function) commands = ([ 
         "lhlo": ehlo, 
         "mail": mail, 
         "rcpt": rcpt, 
         "data": data, 
         "rset": rset, 
         "vrfy": vrfy, 
         "quit": quit, 
         "noop": noop 
  ]); 
  constant protocol = "LMTP"; 
 
  // 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) 
  void message(string content) { 
    if(sizeof(content) > cfg->maxsize) 
    { 
      outcode(552); 
      return 0; 
    } 
    // LMTP as well as SMTP encode '.' by another '.' when it is the first 
    // character of a line so we have to decode it 
    // We don't decode for SMTP since it can be usefull not to encode it for 
    // sending again the mail 
    content = replace(replace(content, "\r\n", "\n"), "\n..", "\n."); 
    MIME.Message message = low_message(content); 
    if(!message) return; 
 
    mixed err; 
    foreach(mailto, string recipient) 
    { 
      int|array check; 
      if(cfg->givedata) 
        err = catch(check = cfg->cb_data(copy_value(message), mailfrom, 
                                    recipient, content)); 
      else 
        err = catch(check = cfg->cb_data(copy_value(message), mailfrom, recipient)); 
      if(err) 
      { 
        outcode(554, err[0]); 
        log(describe_backtrace(err)); 
        continue; 
      } 
      outcode(getretcode(check), geterrorstring(check)); 
    } 
  } 
} 
 
//! A LMTP server. It has been fairly well tested against Postfix client. 
//! Actually this module is only an extention to the @[SMTP] server. 
//! 
//! It implements @rfc{2821@}, @rfc{2822@}, @rfc{2033@} and @rfc{1854@}. 
class Server { 
   protected object fdport; 
   Configuration config; 
 
   protected void accept_callback() 
   { 
     object fd = fdport->accept(); 
     if(!fd) 
       error("Can't accept connections from socket\n"); 
     Connection(fd, config); 
     destruct(fd); 
   } 
 
   //! @decl void create(array(string) _domains, void|int port,@ 
   //!        void|string ip, function _cb_mailfrom,@ 
   //!        function _cb_rcptto, function _cb_data) 
   //!  Create a receiving LMTP server. 
   //! 
   //! @param domain 
   //!   Domains name this server relay, you need to provide at least one 
   //!   domain (the first one will be used for MAILER-DAEMON address). 
   //!   if you want to relay everything you can put a '*' after this 
   //!   first domain. 
   //! @param port 
   //!   Port this server listen on 
   //! @param listenip 
   //!   IP on which server listen 
   //! @param cb_mailfrom 
   //!   Mailfrom callback function, this function will be called 
   //!   when a client send a mail from command. This function must take a 
   //!   string as argument (corresponding to the sender's email) and return 
   //!   int corresponding to the SMTP code to output to the client. If you 
   //!   return an array the first element is the SMTP code and the second 
   //!   is the error string to display. 
   //! @param cb_rcptto 
   //!   Same as cb_mailfrom but called when a client sends a rcpt to. 
   //! @param cb_data 
   //!  This function is called for each recipient in the "rcpt to" command 
   //!  after the client sends the "data" command 
   //!  It must have the following synopsis: 
   //!  int|array cb_data(object mime, string sender, string recipient, void|string rawdata) 
   //!  object mime : the mime data object 
   //!  string sender : sender of the mail (from the mailfrom command) 
   //!  string recipient : one recipient given by one rcpt 
   //!     command. 
   //! return : SMTP code to output to the client. If you return an array 
   //!   the first element is the SMTP code and the second is the error string 
   //!   to display. Note that to comply with LMTP protocol you must output a 
   //!   code each time this function is called. 
   //! @example 
   //!  Here is an example of silly program that does nothing except outputing 
   //!  informations to stdout. 
   //! int cb_mailfrom(string mail) 
   //! { 
   //!   return 250; 
   //! } 
   //! 
   //! int cb_rcptto(string email) 
   //! { 
   //!   // check the user's mailbox here 
   //!   return 250; 
   //! } 
   //! 
   //! int cb_data(object mime, string sender, string recipient) 
   //! { 
   //!   write(sprintf("smtpd: mailfrom=%s, to=%s, headers=%O\ndata=%s\n", 
   //!   sender, recipient, mime->headers, mime->getdata())); 
   //!   // check the data and deliver the mail here 
   //!   if(mime->body_parts) 
   //!   { 
   //!   { 
   //!     foreach(mime->body_parts, object mpart) 
   //!       write(sprintf("smtpd: mpart data = %O\n", mpart->getdata())); 
   //!   } 
   //!   return 250; 
   //! } 
   //! 
   //! int main(int argc, array(string) argv) 
   //! { 
   //!   Protocols.LMTP.Server(({ "ece.fr" }), 2500, "127.0.0.1", @ 
   //!      cb_mailfrom, cb_rcptto, cb_data); 
   //!   return -1; 
   //! } 
   void create(array(string) _domains, void|int port, void|string ip, function _cb_mailfrom, function _cb_rcptto, function _cb_data) 
   { 
     config = Configuration(_domains, _cb_mailfrom, _cb_rcptto, _cb_data); 
     if(!port) 
       port = 25; 
     fdport = Stdio.Port(port, accept_callback, ip); 
     if(!fdport) 
     { 
       error("Cannot bind to socket, already bound ?\n"); 
     } 
   } 
}