Eliminated searchtree debugging messages; Added UNIX socket (named sockets) support...
[fmail.git] / backends / protocol / pop3.cpp
blobdca0449cc05168c78d801b7752a47c3a6d7ba503
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 Job {
78 SearchTree<int, 27, &knhash> cmdtree;
79 Socket * s;
80 public:
81 POP3Handler(Socket *sock) : Job() {
82 /* Insert POP3 commands to search tree, 4 letters only for optimization */
83 cmdtree.Insert("USER", 1);
84 cmdtree.Insert("PASS", 2);
85 cmdtree.Insert("STAT", 3);
86 cmdtree.Insert("LIST", 4);
87 cmdtree.Insert("RETR", 5);
88 cmdtree.Insert("DELE", 6);
89 cmdtree.Insert("NOOP", 7);
90 cmdtree.Insert("QUIT", 8);
91 cmdtree.Insert("RSET", 9);
92 cmdtree.Insert("TOP", 10);
93 cmdtree.Insert("CAPA", 11);
94 printf("SearchTree Memory Usage: %i bytes\n", cmdtree.getTreeSize());
95 this->s = sock;
97 ~POP3Handler(){
98 delete s;
100 int Run(){
101 char buffer[255];
102 //char userbuff[255];
103 //char outbuff[255];
104 int r, i, j;
105 int onrun, cmd, state;
106 //IPC * ipc, * ipcmb;
107 //IPCMessage * msg;
108 string user, pass;
109 pcrecpp::RE reg("[\\w\\a]*\\s([\\w\\a]*)");
111 //ipc = IPC::CreateIPC("socket://127.0.0.1:14000");
112 //ipcmb = IPC::CreateIPC("socket://127.0.0.1:14001");
114 /* Send greeting to client */
115 s->Write(welcome, strlen(welcome));
116 s->Write(CRLF, 2);
118 state = STATE_AUTH;
120 onrun = 1;
122 while (onrun){
123 /* Wait for client input */
124 memset(buffer, 0, 255);
125 r = s->Read(buffer, 255);
127 /* Lookup the command in out command search tree */
128 try{
129 cmd = cmdtree.Lookup(buffer);
130 } catch (SearchNotFound){
131 cmd = -1;
134 if (cmd == CMD_QUIT){
135 printf("Got QUIT\n");
137 s->Write( "+OK", 3);
138 s->Write( CRLF, 2);
140 /* Change state to UPDATE, will quit after that */
141 state = STATE_UPDATE;
144 switch (state){
145 case STATE_AUTH:
146 if (cmd == CMD_USER){
147 /* Parse username from command */
148 reg.PartialMatch(buffer, &user);
149 printf("Got USERNAME: %s\n", user.c_str());
151 s->Write( "+OK", 3);
152 s->Write( CRLF, 2);
154 /* Change our state to expect password */
155 state = STATE_AUTH2;
156 } else{
157 printf("INVALID [AUTH]: %s\n", buffer);
158 s->Write( "-ERR", 4);
159 s->Write( CRLF, 2);
161 break;
162 case STATE_AUTH2:
163 if (cmd == CMD_PASS){
164 /* Extract password from command */
165 reg.PartialMatch(buffer, &pass);
167 printf("Got Password: %s\n", pass.c_str());
169 if (1){
170 s->Write( "+OK", 3);
171 s->Write( CRLF, 2);
173 /* If we got a positive response move to next state */
174 state = STATE_TRANSACTION;
175 } else{
176 s->Write( "-ERR", 4);
177 s->Write(CRLF, 2);
179 /* Authentication failed, change to initial state */
180 state = STATE_AUTH;
183 } else{
184 printf("INVALID [AUTH2]: %s\n", buffer);
185 s->Write( "-ERR", 4);
186 s->Write(CRLF, 2);
187 state = STATE_AUTH;
189 break;
190 case STATE_TRANSACTION:
191 switch (cmd){
192 case CMD_STAT:
193 /* Return server status (OK) */
194 printf("Got STAT Command\n");
195 s->Write( "+OK 3 400", 9);
196 s->Write( CRLF, 2);
197 break;
198 case CMD_LIST:
199 /* TODO: Consult the mailbox server and send the real list */
200 printf("Got LIST Command: %s\n", buffer);
201 s->Write( "+OK", 3);
202 s->Write( CRLF, 2);
204 s->Write( "1 150", 5);
205 s->Write( CRLF, 2);
207 s->Write( "2 150", 5);
208 s->Write( CRLF, 2);
210 s->Write( "3 100", 5);
211 s->Write( CRLF, 2);
212 s->Write( ".", 1);
213 s->Write( CRLF, 2);
214 break;
215 case CMD_RETR:
216 /* TODO: Retrieve the real message from mailbox */
217 printf("Got RETR Command: %s\n", buffer);
218 s->Write( "+OK 150 octets", 14);
219 s->Write( CRLF, 2);
221 s->Write( demomsg, strlen(demomsg));
223 s->Write( CRLF, 2);
224 s->Write( ".", 1);
225 s->Write( CRLF, 2);
226 break;
227 case CMD_DELE:
228 /* TODO: Delete the real message from mailbox */
229 printf("Got DELE Command: %s\n", buffer);
230 s->Write( "+OK", 3);
231 s->Write( CRLF, 2);
232 break;
233 case CMD_NOOP:
234 printf("Got NOOP Command\n");
235 s->Write( "+OK", 3);
236 s->Write( CRLF, 2);
237 break;
238 case CMD_RSET:
239 printf("Got RSET Command\n");
240 s->Write( "+OK", 3);
241 s->Write( CRLF, 2);
242 case CMD_TOP:
243 /* TODO: Return the real top messages from mailbox */
244 printf("Got TOP Command: %s\n", buffer);
245 for (i = strlen(buffer); i > 0; i--){
246 if (buffer[i] == ' ')
247 break;
249 if (i >= 0){
250 i = atoi((char *)(buffer + i));
251 } else{
252 i = 0;
254 s->Write( "+OK", 3);
255 s->Write( CRLF, 2);
257 j =0;
258 for (r = 0; r < i; r++){
259 sscanf((char *)(demomsg+j), "%s", buffer);
260 j = j + strlen(buffer);
261 s->Write( buffer, strlen(buffer));
262 s->Write( CRLF, 2);
264 s->Write( ".", 1);
265 s->Write( CRLF, 2);
266 break;
267 default:
268 printf("INVALID [TRANS]: %s\n", buffer);
269 s->Write( "-ERR", 4);
270 s->Write( CRLF, 2);
272 break;
273 case STATE_UPDATE:
274 onrun = 0;
275 break;
279 s->Close();
280 return 0;
284 class SimpleLoad : public LoadHandler {
285 public:
286 int Dispatch(Socket * sock, ProtocolHandler * ph){
287 if (ph)
288 return ph->Handle(sock);
289 return 0;
293 int main(){
294 Socket * s, * client;
295 Boss *boss;
296 int ret;
298 /* Create our POP3 Handler and Threaded Load Handler */
299 //pop3 = new POP3Handler();
300 //tlh = new ThreadLoad(pop3);
301 boss = new Boss(10);
303 /* Create and bind our socket to POP3 port */
304 s = Socket::CreateSocket(SOCKET_INET, 0);
305 s->setPort(POP3_PORT);
306 s->setAddress("127.0.0.1");
307 s->Bind();
308 s->Listen(15);
310 while(1){
311 /* Poll for incoming connections */
312 ret = s->Poll(1000000, SOCKET_POLL_READ | SOCKET_POLL_ERROR);
314 if (ret & SOCKET_POLL_READ){
315 /* Accept client and dispatch */
316 client = s->Accept();
317 boss->Dispatch(new POP3Handler(client));
318 } else if(ret & SOCKET_POLL_ERROR){ //error
319 return -1;
320 } else{ //timeout
321 break;
324 delete boss;
325 return 0;