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.
27 const char CRLF
[2] = { 0x0D, 0x0A };
31 /* Currently supported SMTP commands */
32 const int CMD_EHLO
= 1;
33 const int CMD_HELO
= 2;
34 const int CMD_MAIL
= 3;
35 const int CMD_RCPT
= 4;
36 const int CMD_DATA
= 5;
37 const int CMD_QUIT
= 6;
38 const int CMD_RSET
= 7;
39 const char KEY_END_DATA
[6] = { 0x0D, 0x0A, '.', 0x0D, 0x0A, 0x00 };
41 /* Create a hash number from a character for search tree */
43 if ((v
>= 'A') && (v
<= 'Z'))
49 if ((v
>= 'a') && (v
<= 'z'))
61 int genPath(char *buffer
){
68 gtime
= gmtime(&ctime
);
70 /* Generate a Random number */
73 /* Construct a unique filename for the slot */
74 sprintf(buffer
, "/tmp/fmail/queue/incoming/%i%i%i%i%i", gtime
->tm_yday
, gtime
->tm_hour
, gtime
->tm_min
, gtime
->tm_sec
, r
);
79 class SMTPHandler
: public Job
{
80 SearchTree
< int, 27, &knhash
> cmdtree
;
81 std::string queue_ipc
;
84 SMTPHandler (std::string qipc
, Socket
*sock
) : Job() {
85 /* Queue IPC string */
90 /* Insert the SMTP commands to search tree */
91 cmdtree
.Insert ("EHLO", 1);
92 cmdtree
.Insert ("HELO", 2);
93 cmdtree
.Insert ("MAIL", 3);
94 cmdtree
.Insert ("RCPT", 4);
95 cmdtree
.Insert ("DATA", 5);
96 cmdtree
.Insert ("QUIT", 6);
97 cmdtree
.Insert ("RSET", 7);
103 char buffer
[255], * slotname
;
105 int onrun
, cmd
, state
;
107 pcrecpp::RE
reCmd ("\\w*:<(.*)>");
111 string mailfrom
, rcpt
;
113 /* Write out the SMTP server Greeting */
114 s
->Write ("220 FancyMail v0.1", 18);
118 /* Wait for client input */
119 memset (buffer
, 0, 255);
120 r
= s
->Read (buffer
, 255);
122 /* Find the given command on our search tree */
124 cmd
= cmdtree
.Lookup (buffer
);
125 } catch (SearchNotFound
) {
131 /* We got normal SMTP HELO, write response */
132 s
->Write ("250 ok localhost", 16);
135 /* Client presented itself, we can accept other
136 * commands, we pass to state 1 */
140 /* We still not support ESMTP, write not implemented */
141 s
->Write ("502 Not implemented", 19);
145 /* Check if we had the proper greeting */
147 s
->Write ("502 Not implemented", 19);
152 /* Extract the command mail from data */
153 ret
= reCmd
.PartialMatch (buffer
, &mailfrom
);
156 s
->Write ("250signal(SIGTERM, SIG_IGN); OK", 6);
159 /* We pass to next state */
163 /* Check that we have recieved the MAIL command first */
165 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);
194 /* Open Queue Slot File */
196 fd
= fopen (slotname
, "w");
200 printf ("Error: Unable to open destination SLOT\n");
205 /* Write our headers */
206 fprintf (fd
, "MAIL FROM: %s\n", mailfrom
.c_str ());
207 fprintf (fd
, "RCPT: %s\n", rcpt
.c_str ());
212 /* Read incoming client data */
213 memset (buffer
, 0, 255);
214 l
= s
->Read (buffer
, 255);
216 /* Write to slot file */
217 fwrite (buffer
, 1, l
, fd
);
220 if (strstr (buffer
, KEY_END_DATA
)) {
225 /* Close our slot file */
228 s
->Write ("250 OK", 6);
231 /* Go back to initial state */
235 s
->Write ("250 OK", 6);
242 s
->Write ("221 Exiting", 11);
249 printf ("Not implemented [State: %i]: %s\n", state
, buffer
);
250 s
->Write ("502 Not implemented", 19);
263 class SimpleLoad
: public LoadHandler
{
265 int Dispatch (Socket
* sock
, ProtocolHandler
* ph
) {
267 return ph
->Handle (sock
);
272 void onExit(int sig
){
273 printf("Got Signal %i\n", sig
);
275 signal(SIGTERM
, SIG_IGN
);
276 signal(SIGINT
, SIG_IGN
);
282 int ret
, port
, tcount
;
283 std::string conf_handler
, qipc
;
284 Configuration
conf ("smtp.conf");
285 struct sigaction act
;
287 act
.sa_handler
= onExit
;
288 sigemptyset(&act
.sa_mask
);
291 sigaction(SIGINT
, &act
, 0);
293 /* Get basic configuration options */
294 port
= conf
.getInt ("port", 25);
295 conf_handler
= conf
.getString ("loadhandler", "simple");
297 /* Create a Socket and bind it to SMTP port */
298 s
= Socket::CreateSocket (SOCKET_INET
, 0);
299 s
->setAddress (NULL
);
303 printf ("Unable to bind to port\n");
309 /* Load queue connection string */
310 qipc
= conf
.getString ("queue", "socket://127.0.0.1:14003");
312 /* Create a Boss for Thread Worker based on configuration */
313 if (conf_handler
== "thread") {
314 /* If we got threaded loadhandler check for max worker thread count */
315 tcount
= conf
.getInt ("thread.count", 10);
316 boss
= new Boss(tcount
);
324 /* Poll for incoming connections */
325 ret
= s
->Poll (1000000, SOCKET_POLL_READ
| SOCKET_POLL_ERROR
);
326 if (ret
& SOCKET_POLL_READ
) {
327 /* Accept client and dispatch */
329 boss
->Dispatch(new SMTPHandler (qipc
, cl
));
330 } else if (ret
& SOCKET_POLL_ERROR
) { //error
337 printf("fmail-smtp shutting down\n");