Updated and cleaned SMTP code, added comments and queue support
[fmail.git] / backends / protocol / smtp.cpp
blobcf59071b18743e93c628a5d2913f7de61ed39a24
1 /*
2 SMTP Protocol Handler
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.
21 #include <libfmail.h>
22 #include <pcrecpp.h>
24 const int SMTP_PORT = 12000; //25;
25 const char CRLF[2] = {0x0D, 0x0A};
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 int knhash(char v){
37 if ((v >= 65) && (v <= 90))
38 return v - 65;
40 if (v == 95)
41 return 26;
43 if ((v >= 97) && (v <= 122))
44 return v - 97;
46 if (v == ' ')
47 return -1;
49 if (v == 0x0D)
50 return -1;
52 return 255;
55 class SMTPHandler : public ProtocolHandler{
56 SearchTree<int, 27, &knhash> cmdtree;
57 public:
58 SMTPHandler(){
59 cmdtree.Insert("EHLO", 1);
60 cmdtree.Insert("HELO", 2);
61 cmdtree.Insert("MAIL", 3);
62 cmdtree.Insert("RCPT", 4);
63 cmdtree.Insert("DATA", 5);
64 cmdtree.Insert("QUIT", 6);
65 cmdtree.Insert("RSET", 7);
67 int Handle(Socket *s){
68 char buffer[255], *slotname;
69 int r, l, ret;
70 int onrun, cmd, state;
71 FILE *fd;
72 pcrecpp::RE reCmd("\\w*:<(.*)>");
73 state = 0;
74 onrun = 1;
75 cmd = 0;
76 string mailfrom, rcpt;
78 IPC *ipc;
79 IPCMessage *msg;
81 /* Write out the SMTP server Greeting */
82 s->Write("220 FancyMail v0.1", 18);
83 s->Write( CRLF, 2);
85 while (onrun){
86 /* Wait for client input */
87 memset(buffer, 0, 255);
88 r = s->Read(buffer, 255);
90 /* Find the given command on our search tree */
91 try{
92 cmd = cmdtree.Lookup(buffer);
93 }catch (SearchNotFound){
94 cmd = -1;
97 switch(cmd){
98 case CMD_HELO:
99 /* We got normal SMTP HELO, write response */
100 s->Write("250 ok localhost", 16);
101 s->Write(CRLF, 2);
103 /* Client presented itself, we can accept other
104 * commands, we pass to state 1 */
105 state = 1;
106 break;
107 case CMD_EHLO:
108 /* We still not support ESMTP, write not implemented */
109 s->Write( "502 Not implemented", 19);
110 s->Write( CRLF, 2);
111 break;
112 case CMD_MAIL:
113 /* Check if we had the proper greeting */
114 if (state != 1){
115 s->Write( "502 Not implemented", 19);
116 s->Write( CRLF, 2);
117 break;
120 /* Extract the command mail from data */
121 ret = reCmd.PartialMatch(buffer, &mailfrom);
122 printf("[Debug] Sending Mail From: %s\n", mailfrom.c_str());
124 /* Write Response */
125 s->Write( "250 OK", 6);
126 s->Write( CRLF, 2);
128 /* We pass to next state */
129 state = 2;
130 break;
131 case CMD_RCPT:
132 /* Check that we have recieved the MAIL command first */
133 if (state != 2){
134 s->Write( "502 Not implemented", 19);
135 s->Write( CRLF, 2);
136 break;
139 /* Extract the recipent */
140 reCmd.PartialMatch(buffer, &rcpt);
141 printf("[Debug] Recipent: %s\n", rcpt.c_str());
143 s->Write( "250 OK", 6);
144 s->Write( CRLF, 2);
146 state = 3;
147 break;
148 case CMD_DATA:
149 /* Check that we have recived the RCPT command first */
150 if (state != 3){
151 s->Write( "502 Not implemented", 19);
152 s->Write( CRLF, 2);
153 break;
156 /* Give the client green light to send its data */
157 printf("Reciving Data!\n");
158 s->Write( "354 OK", 6);
159 s->Write( CRLF, 2);
161 /* Connect to the Queue server */
162 ipc = IPC::CreateIPC("socket://127.0.0.1:14002");
163 ipc->RequestIPC();
165 /* Request a slot */
166 msg = new IPCMessage("slot");
167 msg->PushParam("incoming");
168 ipc->PushMessage(msg);
170 /* Retrieve Response */
171 msg = ipc->PopMessage();
172 if (!strcmp(msg->GetMessageName(), "ok"){
173 slotname = msg->PopParam();
174 }else{
175 break;
178 /* Open Queue Slot File */
179 fd = fopen(slotname, "w");
181 /* Write our headers */
182 fprintf(fd, "MAIL FROM: %s\n", mailfrom.c_str());
183 fprintf(fd, "RCPT: %s\n", rcp.c_str());
184 fprintf(fd, "\n");
186 r = 1;
187 while (r){
188 /* Read incoming client data */
189 memset(buffer, 0, 255);
190 l = s->Read( buffer, 255);
192 /* Write to slot file */
193 fwrite(buffer, 1, l, fd);
195 /* Check for EOF */
196 if (strstr(buffer, KEY_END_DATA)){
197 r = 0;
198 printf("\nEnd of Data\n");
199 }else{
200 printf("Buffer[%i]: %s\n", l, buffer);
204 /* Close our slot file */
205 fclose(fd);
207 /* Push the message to the Queue */
208 msg = new IPCMessage("push");
209 msg->PushParam("incoming");
210 msg->PushParam(slotname);
211 ipc->PushMessage(msg);
213 /* Close IPC*/
214 ipc->Close();
216 s->Write( "250 OK", 6);
217 s->Write( CRLF, 2);
219 /* Go back to initial state */
220 state = 1;
221 break;
222 case CMD_RSET:
223 s->Write( "250 OK", 6);
224 s->Write( CRLF, 2);
226 /* Reset state */
227 state = 1;
228 break;
229 case CMD_QUIT:
230 printf("Got Quit, Exiting\n");
231 s->Write( "221 Exiting", 11);
232 s->Write(CRLF, 2);
234 /* Stop main loop */
235 onrun = 0;
236 break;
237 default:
238 printf("Not implemented [State: %i]: %s\n", state, buffer);
239 s->Write( "502 Not implemented", 19);
240 s->Write( CRLF, 2);
241 break;
244 printf("[COMMAND]: %s\n", buffer);
247 /* Close Connection */
248 printf("Closing Connection\n");
249 s->Close();
253 class SimpleLoad : public LoadHandler {
254 public:
255 int Dispatch(Socket *sock, ProtocolHandler *ph){
256 if (ph)
257 return ph->Handle(sock);
258 return 0;
262 int main(int argc, char **argv){
263 ThreadLoad *tlh;
264 SMTPHandler *handler;
265 Socket *s, *cl;
266 int ret;
268 /* Create a Socket and bind it to SMTP port */
269 s = Socket::CreateSocket(SOCKET_INET, 0);
270 s->setAddress("localhost");
271 s->setPort(SMTP_PORT);
272 s->Bind();
273 s->Listen(15);
275 /* Create a handler object */
276 handler = new SMTPHandler();
278 /* Create a LoadHandler Object*/
279 tlh = new ThreadLoad(handler);
281 while(1){
282 /* Poll for incoming connections */
283 ret = s->Poll(1000000, SOCKET_POLL_READ);
284 if (ret == SOCKET_POLL_READ){
285 /* Accept client and dispatch */
286 cl = s->Accept();
287 tlh->Dispatch(cl, handler);
291 return 0;