4 Copyright (C) 2007 Carlos Daniel Ruvalcaba Valenzuela <clsdaniel@gmail.com>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 const char CRLF
[2] = {0x0D, 0x0A};
26 /* Currently supported SMTP commands */
27 const int CMD_EHLO
= 1;
28 const int CMD_HELO
= 2;
29 const int CMD_MAIL
= 3;
30 const int CMD_RCPT
= 4;
31 const int CMD_DATA
= 5;
32 const int CMD_QUIT
= 6;
33 const int CMD_RSET
= 7;
34 const char KEY_END_DATA
[6] = {0x0D, 0x0A, '.', 0x0D, 0x0A, 0x00};
36 /* Create a hash number from a character for search tree */
38 if ((v
>= 'A') && (v
<= 'Z'))
44 if ((v
>= 'a') && (v
<= 'z'))
56 class SMTPHandler
: public ProtocolHandler
{
57 SearchTree
<int, 27, &knhash
> cmdtree
;
58 std::string queue_ipc
;
60 SMTPHandler(std::string qipc
){
61 /* Queue IPC string */
64 /* Insert the SMTP commands to search tree */
65 cmdtree
.Insert("EHLO", 1);
66 cmdtree
.Insert("HELO", 2);
67 cmdtree
.Insert("MAIL", 3);
68 cmdtree
.Insert("RCPT", 4);
69 cmdtree
.Insert("DATA", 5);
70 cmdtree
.Insert("QUIT", 6);
71 cmdtree
.Insert("RSET", 7);
73 int Handle(Socket
*s
){
74 char buffer
[255], *slotname
;
76 int onrun
, cmd
, state
;
78 pcrecpp::RE
reCmd("\\w*:<(.*)>");
82 string mailfrom
, rcpt
;
87 /* Write out the SMTP server Greeting */
88 s
->Write("220 FancyMail v0.1", 18);
91 /* Connect to the Queue server */
92 ipc
= IPC::CreateIPC((char*)queue_ipc
.c_str());
96 /* Wait for client input */
97 memset(buffer
, 0, 255);
98 r
= s
->Read(buffer
, 255);
100 /* Find the given command on our search tree */
102 cmd
= cmdtree
.Lookup(buffer
);
103 }catch (SearchNotFound
){
109 /* We got normal SMTP HELO, write response */
110 s
->Write("250 ok localhost", 16);
113 /* Client presented itself, we can accept other
114 * commands, we pass to state 1 */
118 /* We still not support ESMTP, write not implemented */
119 s
->Write( "502 Not implemented", 19);
123 /* Check if we had the proper greeting */
125 s
->Write( "502 Not implemented", 19);
130 /* Extract the command mail from data */
131 ret
= reCmd
.PartialMatch(buffer
, &mailfrom
);
134 s
->Write( "250 OK", 6);
137 /* We pass to next state */
141 /* Check that we have recieved the MAIL command first */
143 s
->Write( "502 Not implemented", 19);
148 /* Extract the recipent */
149 reCmd
.PartialMatch(buffer
, &rcpt
);
151 s
->Write( "250 OK", 6);
157 /* Check that we have recived the RCPT command first */
159 s
->Write( "502 Not implemented", 19);
164 /* Give the client green light to send its data */
165 s
->Write( "354 OK", 6);
169 msg
= new IPCMessage("slot");
170 msg
->PushParam("incoming");
171 ipc
->SendMessage(msg
);
174 /* Retrieve Response */
175 msg
= ipc
->ReciveMessage();
176 if (!strcmp(msg
->GetMessageName(), "ok")){
177 slotname
= msg
->PopParam();
182 /* Open Queue Slot File */
184 fd
= fopen(slotname
, "w");
188 printf("Error: Unable to open destination SLOT\n");
193 /* Write our headers */
194 fprintf(fd
, "MAIL FROM: %s\n", mailfrom
.c_str());
195 fprintf(fd
, "RCPT: %s\n", rcpt
.c_str());
200 /* Read incoming client data */
201 memset(buffer
, 0, 255);
202 l
= s
->Read( buffer
, 255);
204 /* Write to slot file */
205 fwrite(buffer
, 1, l
, fd
);
208 if (strstr(buffer
, KEY_END_DATA
)){
213 /* Close our slot file */
218 /* Push the message to the Queue */
219 msg
= new IPCMessage("push");
220 msg
->PushParam("incoming");
221 msg
->PushParam(slotname
);
222 ipc
->SendMessage(msg
);
227 s
->Write( "250 OK", 6);
230 /* Go back to initial state */
234 s
->Write( "250 OK", 6);
241 printf("GOT QUIT\n");
242 s
->Write( "221 Exiting", 11);
249 printf("Not implemented [State: %i]: %s\n", state
, buffer
);
250 s
->Write( "502 Not implemented", 19);
257 /* Close Connection */
258 msg
= new IPCMessage("quit");
259 ipc
->SendMessage(msg
);
268 class SimpleLoad
: public LoadHandler
{
270 int Dispatch(Socket
*sock
, ProtocolHandler
*ph
){
272 return ph
->Handle(sock
);
279 SMTPHandler
*handler
;
281 int ret
, port
, tcount
;
282 std::string conf_handler
, qipc
;
283 Configuration
conf("smtp.conf");
285 /* Get basic configuration options */
286 port
= conf
.getInt("port", 25);
287 conf_handler
= conf
.getString("loadhandler", "simple");
289 /* Create a Socket and bind it to SMTP port */
290 s
= Socket::CreateSocket(SOCKET_INET
, 0);
295 printf("Unable to bind to port\n");
301 /* Load queue connection string */
302 qipc
= conf
.getString("queue", "socket://127.0.0.1:14003");
304 /* Create a handler object */
305 handler
= new SMTPHandler(qipc
);
307 /* Create a LoadHandler Object based on configuration */
308 if (conf_handler
== "thread"){
309 /* If we got threaded loadhandler check for max worker thread count */
310 tcount
= conf
.getInt("thread.count", 10);
311 lh
= new ThreadLoad(handler
, tcount
);
313 lh
= new SimpleLoad();
316 /* TODO: Don't loop forever, have a condition. Options:
317 * * We need to handle SIGTERM signal, but this may not be
318 * crossplatform, atexit may work on windows too.
319 * * Handle a EXIT ipc message (may lack security).
322 /* Poll for incoming connections */
323 ret
= s
->Poll(1000000, SOCKET_POLL_READ
| SOCKET_POLL_ERROR
);
324 if (ret
& SOCKET_POLL_READ
){
325 /* Accept client and dispatch */
326 printf("Dispatching Client\n");
328 lh
->Dispatch(cl
, handler
);
329 printf("On to next client!\n");
330 }else if(ret
& SOCKET_POLL_ERROR
){ //error