Added signal handling to smtp, nailed many memory leaks on SearchTree, WorkerThread...
[fmail.git] / backends / protocol / pop3.cpp
bloba7fd611c811f74ce36e172f22457e1011e0f7ea8
1 /*
2 POP3 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 char * demomsg = "From: Test John <testjohn@localhost>\n"
25 "To: test@localhost\n"
26 "Content-Type: text/plain\n"
27 "Date: Wed, 06 Jun 2007 15:21:21 -0700\n"
28 "Message-Id: <1181168481.15047.2.camel@localhost>\n"
29 "Mime-Version: 1.0\n"
30 "X-Mailer: Evolution 2.10.1 \n"
31 "Content-Transfer-Encoding: 7bit\n"
32 "\n"
33 "Test Mail!!";
35 const int POP3_PORT = 12001; //25;
36 const char CRLF[2] = {0x0D, 0x0A};
37 const char *welcome = "+OK FancyMail v0.1";
39 /* Pop3 Commands */
40 const int CMD_USER = 1;
41 const int CMD_PASS = 2;
42 const int CMD_STAT = 3;
43 const int CMD_LIST = 4;
44 const int CMD_RETR = 5;
45 const int CMD_DELE = 6;
46 const int CMD_NOOP = 7;
47 const int CMD_QUIT = 8;
48 const int CMD_RSET = 9;
49 const int CMD_TOP = 10;
50 const int CMD_CAPA = 10;
52 const char KEY_END_DATA[6] = {0x0D, 0x0A, '.', 0x0D, 0x0A, 0x00};
54 /* Server state */
55 enum {STATE_AUTH = 1, STATE_AUTH2 = 2, STATE_TRANSACTION = 3,
56 STATE_UPDATE = 4};
58 int knhash(char v){
59 if ((v >= 'A') && (v <= 'Z'))
60 return v - 'A';
62 if (v == '_')
63 return 26;
65 if ((v >= 'a') && (v <= 'z'))
66 return v - 'a';
68 if (v == ' ')
69 return -1;
71 if (v == 0x0D)
72 return -1;
74 return 255;
77 class POP3Handler : public ProtocolHandler {
78 SearchTree<int, 27, &knhash> cmdtree;
79 public:
80 POP3Handler(){
81 /* Insert POP3 commands to search tree, 4 letters only for optimization */
82 cmdtree.Insert("USER", 1);
83 cmdtree.Insert("PASS", 2);
84 cmdtree.Insert("STAT", 3);
85 cmdtree.Insert("LIST", 4);
86 cmdtree.Insert("RETR", 5);
87 cmdtree.Insert("DELE", 6);
88 cmdtree.Insert("NOOP", 7);
89 cmdtree.Insert("QUIT", 8);
90 cmdtree.Insert("RSET", 9);
91 cmdtree.Insert("TOP", 10);
92 cmdtree.Insert("CAPA", 11);
93 printf("SearchTree Memory Usage: %i bytes\n", cmdtree.getTreeSize());
95 int Handle(Socket * s){
96 char buffer[255];
97 //char userbuff[255];
98 //char outbuff[255];
99 int r, i, j;
100 int onrun, cmd, state;
101 IPC * ipc, * ipcmb;
102 IPCMessage * msg;
103 string user, pass;
104 pcrecpp::RE reg("[\\w\\a]*\\s([\\w\\a]*)");
106 ipc = IPC::CreateIPC("socket://127.0.0.1:14000");
107 ipcmb = IPC::CreateIPC("socket://127.0.0.1:14001");
109 /* Send greeting to client */
110 s->Write(welcome, strlen(welcome));
111 s->Write(CRLF, 2);
113 state = STATE_AUTH;
115 onrun = 1;
117 while (onrun){
118 /* Wait for client input */
119 memset(buffer, 0, 255);
120 r = s->Read(buffer, 255);
122 /* Lookup the command in out command search tree */
123 try{
124 cmd = cmdtree.Lookup(buffer);
125 } catch (SearchNotFound){
126 cmd = -1;
129 if (cmd == CMD_QUIT){
130 printf("Got QUIT\n");
132 s->Write( "+OK", 3);
133 s->Write( CRLF, 2);
135 /* Change state to UPDATE, will quit after that */
136 state = STATE_UPDATE;
139 switch (state){
140 case STATE_AUTH:
141 if (cmd == CMD_USER){
142 /* Parse username from command */
143 reg.PartialMatch(buffer, &user);
144 printf("Got USERNAME: %s\n", user.c_str());
146 s->Write( "+OK", 3);
147 s->Write( CRLF, 2);
149 /* Change our state to expect password */
150 state = STATE_AUTH2;
151 } else{
152 printf("INVALID [AUTH]: %s\n", buffer);
153 s->Write( "-ERR", 4);
154 s->Write( CRLF, 2);
156 break;
157 case STATE_AUTH2:
158 if (cmd == CMD_PASS){
159 /* Extract password from command */
160 reg.PartialMatch(buffer, &pass);
162 printf("Got Password: %s\n", pass.c_str());
164 /* Request IPC Session with authentication server */
165 i = 0;
166 while (i < 10){
167 if (ipc->RequestIPC())
168 i = 10;
171 /* Send the auth message */
172 msg = new IPCMessage("auth");
173 msg->PushParam((char *)user.c_str());
174 msg->PushParam((char *)pass.c_str());
175 ipc->PushMessage(msg);
177 while(ipc->PeekMessage() == 0){
179 msg = ipc->PopMessage();
181 ipc->CloseIPC();
183 if (!strcmp(msg->GetMessageName(), "authok")){
184 s->Write( "+OK", 3);
185 s->Write( CRLF, 2);
187 /* If we got a positive response move to next state */
188 state = STATE_TRANSACTION;
189 } else{
190 s->Write( "-ERR", 4);
191 s->Write(CRLF, 2);
193 /* Authentication failed, change to initial state */
194 state = STATE_AUTH;
197 } else{
198 printf("INVALID [AUTH2]: %s\n", buffer);
199 s->Write( "-ERR", 4);
200 s->Write(CRLF, 2);
201 state = STATE_AUTH;
203 break;
204 case STATE_TRANSACTION:
205 switch (cmd){
206 case CMD_STAT:
207 /* Return server status (OK) */
208 printf("Got STAT Command\n");
209 s->Write( "+OK 3 400", 9);
210 s->Write( CRLF, 2);
211 break;
212 case CMD_LIST:
213 /* TODO: Consult the mailbox server and send the real list */
214 printf("Got LIST Command: %s\n", buffer);
215 s->Write( "+OK", 3);
216 s->Write( CRLF, 2);
218 s->Write( "1 150", 5);
219 s->Write( CRLF, 2);
221 s->Write( "2 150", 5);
222 s->Write( CRLF, 2);
224 s->Write( "3 100", 5);
225 s->Write( CRLF, 2);
226 s->Write( ".", 1);
227 s->Write( CRLF, 2);
228 break;
229 case CMD_RETR:
230 /* TODO: Retrieve the real message from mailbox */
231 printf("Got RETR Command: %s\n", buffer);
232 s->Write( "+OK 150 octets", 14);
233 s->Write( CRLF, 2);
235 s->Write( demomsg, strlen(demomsg));
237 s->Write( CRLF, 2);
238 s->Write( ".", 1);
239 s->Write( CRLF, 2);
240 break;
241 case CMD_DELE:
242 /* TODO: Delete the real message from mailbox */
243 printf("Got DELE Command: %s\n", buffer);
244 s->Write( "+OK", 3);
245 s->Write( CRLF, 2);
246 break;
247 case CMD_NOOP:
248 printf("Got NOOP Command\n");
249 s->Write( "+OK", 3);
250 s->Write( CRLF, 2);
251 break;
252 case CMD_RSET:
253 printf("Got RSET Command\n");
254 s->Write( "+OK", 3);
255 s->Write( CRLF, 2);
256 case CMD_TOP:
257 /* TODO: Return the real top messages from mailbox */
258 printf("Got TOP Command: %s\n", buffer);
259 for (i = strlen(buffer); i > 0; i--){
260 if (buffer[i] == ' ')
261 break;
263 if (i >= 0){
264 i = atoi((char *)(buffer + i));
265 } else{
266 i = 0;
268 s->Write( "+OK", 3);
269 s->Write( CRLF, 2);
271 j =0;
272 for (r = 0; r < i; r++){
273 sscanf((char *)(demomsg+j), "%s", buffer);
274 j = j + strlen(buffer);
275 s->Write( buffer, strlen(buffer));
276 s->Write( CRLF, 2);
278 s->Write( ".", 1);
279 s->Write( CRLF, 2);
280 break;
281 default:
282 printf("INVALID [TRANS]: %s\n", buffer);
283 s->Write( "-ERR", 4);
284 s->Write( CRLF, 2);
286 break;
287 case STATE_UPDATE:
288 onrun = 0;
289 break;
292 printf("Closing Connection\n");
293 s->Close();
294 delete ipcmb;
295 delete ipc;
296 return 0;
300 class SimpleLoad : public LoadHandler {
301 public:
302 int Dispatch(Socket * sock, ProtocolHandler * ph){
303 if (ph)
304 return ph->Handle(sock);
305 return 0;
309 int main(){
310 //BaseServer *srv;
311 //SimpleLoad *lh;
312 POP3Handler * pop3;
313 Socket * s, * client;
314 ThreadLoad * tlh;
315 int ret;
317 /* Create our POP3 Handler and Threaded Load Handler */
318 pop3 = new POP3Handler();
319 tlh = new ThreadLoad(pop3);
321 /* Create and bind our socket to POP3 port */
322 s = Socket::CreateSocket(SOCKET_INET, 0);
323 s->setPort(POP3_PORT);
324 s->setAddress("127.0.0.1");
325 s->Bind();
326 s->Listen(15);
328 while(1){
329 /* Poll for incoming connections */
330 ret = s->Poll(1000000, SOCKET_POLL_READ | SOCKET_POLL_ERROR);
332 if (ret & SOCKET_POLL_READ){
333 /* Accept client and dispatch */
334 client = s->Accept();
335 tlh->Dispatch(client, pop3);
336 } else if(ret & SOCKET_POLL_ERROR){ //error
337 return -1;
338 } else{ //timeout
339 break;
342 delete tlh;
343 delete pop3;
344 return 0;