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.
26 const char CRLF
[2] = { 0x0D, 0x0A };
28 /* Currently supported SMTP commands */
29 const int CMD_EHLO
= 1;
30 const int CMD_HELO
= 2;
31 const int CMD_MAIL
= 3;
32 const int CMD_RCPT
= 4;
33 const int CMD_DATA
= 5;
34 const int CMD_QUIT
= 6;
35 const int CMD_RSET
= 7;
36 const char KEY_END_DATA
[6] = { 0x0D, 0x0A, '.', 0x0D, 0x0A, 0x00 };
38 /* Create a hash number from a character for search tree */
40 if ((v
>= 'A') && (v
<= 'Z'))
46 if ((v
>= 'a') && (v
<= 'z'))
58 int genPath(char *buffer
){
65 gtime
= gmtime(&ctime
);
67 /* Generate a Random number */
70 /* Construct a unique filename for the slot */
71 sprintf(buffer
, "/tmp/fmail/queue/incoming/%i%i%i%i%i", gtime
->tm_yday
, gtime
->tm_hour
, gtime
->tm_min
, gtime
->tm_sec
, r
);
76 class SMTPHandler
: public Job
{
77 SearchTree
< int, 27, &knhash
> cmdtree
;
78 std::string queue_ipc
;
81 SMTPHandler (std::string qipc
, Socket
*sock
) : Job() {
82 /* Queue IPC string */
87 /* Insert the SMTP commands to search tree */
88 cmdtree
.Insert ("EHLO", 1);
89 cmdtree
.Insert ("HELO", 2);
90 cmdtree
.Insert ("MAIL", 3);
91 cmdtree
.Insert ("RCPT", 4);
92 cmdtree
.Insert ("DATA", 5);
93 cmdtree
.Insert ("QUIT", 6);
94 cmdtree
.Insert ("RSET", 7);
97 char buffer
[255], * slotname
;
99 int onrun
, cmd
, state
;
101 pcrecpp::RE
reCmd ("\\w*:<(.*)>");
105 string mailfrom
, rcpt
;
110 /* Write out the SMTP server Greeting */
111 s
->Write ("220 FancyMail v0.1", 18);
114 /* Connect to the Queue server */
115 //ipc = IPC::CreateIPC ((char *) queue_ipc.c_str ());
116 //ipc->RequestIPC ();
119 /* Wait for client input */
120 memset (buffer
, 0, 255);
121 r
= s
->Read (buffer
, 255);
123 /* Find the given command on our search tree */
125 cmd
= cmdtree
.Lookup (buffer
);
126 } catch (SearchNotFound
) {
132 /* We got normal SMTP HELO, write response */
133 s
->Write ("250 ok localhost", 16);
136 /* Client presented itself, we can accept other
137 * commands, we pass to state 1 */
141 /* We still not support ESMTP, write not implemented */
142 s
->Write ("502 Not implemented", 19);
146 /* Check if we had the proper greeting */
148 s
->Write ("502 Not implemented", 19);
153 /* Extract the command mail from data */
154 ret
= reCmd
.PartialMatch (buffer
, &mailfrom
);
157 s
->Write ("250 OK", 6);
160 /* We pass to next state */
164 /* Check that we have recieved the MAIL command first */
166 s
->Write ("502 Not implemented", 19);
171 /* Extract the recipent */
172 reCmd
.PartialMatch (buffer
, &rcpt
);
174 s
->Write ("250 OK", 6);
180 /* Check that we have recived the RCPT command first */
182 s
->Write ("502 Not implemented", 19);
187 /* Give the client green light to send its data */
188 s
->Write ("354 OK", 6);
195 /* msg = new IPCMessage ("slot");
196 msg->PushParam ("incoming");
197 ipc->SendMessage (msg);
200 /* Retrieve Response */
201 /* msg = ipc->ReciveMessage ();
202 if (!strcmp (msg->GetMessageName (), "ok")) {
203 slotname = msg->PopParam ();
208 /* Open Queue Slot File */
210 fd
= fopen (slotname
, "w");
214 printf ("Error: Unable to open destination SLOT\n");
219 /* Write our headers */
220 fprintf (fd
, "MAIL FROM: %s\n", mailfrom
.c_str ());
221 fprintf (fd
, "RCPT: %s\n", rcpt
.c_str ());
226 /* Read incoming client data */
227 memset (buffer
, 0, 255);
228 l
= s
->Read (buffer
, 255);
230 /* Write to slot file */
231 fwrite (buffer
, 1, l
, fd
);
234 if (strstr (buffer
, KEY_END_DATA
)) {
239 /* Close our slot file */
244 /* Push the message to the Queue */
245 /* msg = new IPCMessage ("push");
246 msg->PushParam ("incoming");
247 msg->PushParam (slotname);
248 ipc->SendMessage (msg);*/
253 s
->Write ("250 OK", 6);
256 /* Go back to initial state */
260 s
->Write ("250 OK", 6);
267 s
->Write ("221 Exiting", 11);
274 printf ("Not implemented [State: %i]: %s\n", state
, buffer
);
275 s
->Write ("502 Not implemented", 19);
282 /* Close Connection */
283 /*msg = new IPCMessage ("quit");
284 ipc->SendMessage (msg);
293 class SimpleLoad
: public LoadHandler
{
295 int Dispatch (Socket
* sock
, ProtocolHandler
* ph
) {
297 return ph
->Handle (sock
);
305 int ret
, port
, tcount
;
306 std::string conf_handler
, qipc
;
307 Configuration
conf ("smtp.conf");
309 /* Get basic configuration options */
310 port
= conf
.getInt ("port", 25);
311 conf_handler
= conf
.getString ("loadhandler", "simple");
313 /* Create a Socket and bind it to SMTP port */
314 s
= Socket::CreateSocket (SOCKET_INET
, 0);
315 s
->setAddress (NULL
);
319 printf ("Unable to bind to port\n");
325 /* Load queue connection string */
326 qipc
= conf
.getString ("queue", "socket://127.0.0.1:14003");
328 /* Create a Boss for Thread Worker based on configuration */
329 if (conf_handler
== "thread") {
330 /* If we got threaded loadhandler check for max worker thread count */
331 tcount
= conf
.getInt ("thread.count", 10);
332 boss
= new Boss(tcount
);
337 /* TODO: Don't loop forever, have a condition. Options:
338 * * We need to handle SIGTERM signal, but this may not be
339 * crossplatform, atexit may work on windows too.
340 * * Handle a EXIT ipc message (may lack security).
343 /* Poll for incoming connections */
344 ret
= s
->Poll (1000000, SOCKET_POLL_READ
| SOCKET_POLL_ERROR
);
345 if (ret
& SOCKET_POLL_READ
) {
346 /* Accept client and dispatch */
348 boss
->Dispatch(new SMTPHandler (qipc
, cl
));
349 } else if (ret
& SOCKET_POLL_ERROR
) { //error