- add python, missing gui
[enigma2.git] / lib / network / httpd.cpp
1 // #define DEBUG_HTTPD
2 #include <lib/network/httpd.h>
3
4 #include <sys/socket.h>
5 #include <lib/base/estring.h>
6 #include <error.h>
7 #include <errno.h>
8 #include <time.h>
9 #include <ctype.h>
10
11 eHTTPDataSource::eHTTPDataSource(eHTTPConnection *c): connection(c)
12 {
13 }
14
15 eHTTPDataSource::~eHTTPDataSource()
16 {
17 }
18
19 void eHTTPDataSource::haveData(void *data, int len)
20 {
21 }
22
23 int eHTTPDataSource::doWrite(int)
24 {
25         return 0;
26 }
27
28 eHTTPError::eHTTPError(eHTTPConnection *c, int errcode): eHTTPDataSource(c), errcode(errcode)
29 {
30         std::string error="unknown error";
31         switch (errcode)
32         {
33         case 400: error="Bad Request"; break;
34         case 401: error="Unauthorized"; break;
35         case 403: error="Forbidden"; break;
36         case 404: error="Not found"; break;
37         case 405: error="Method not allowed"; break;
38         case 500: error="Internal server error"; break;
39         }
40         connection->code_descr=error;
41         connection->code=errcode;
42         
43         connection->local_header["Content-Type"]=std::string("text/html");
44 }
45
46 int eHTTPError::doWrite(int w)
47 {
48         std::string html;
49         html+="<html><head><title>Error " + getNum(connection->code) + "</title></head>"+
50                 "<body><h1>Error " + getNum(errcode) + ": " + connection->code_descr + "</h1></body></html>\n";
51         connection->writeBlock(html.c_str(), html.length());
52         return -1;
53 }
54
55 eHTTPConnection::eHTTPConnection(int socket, int issocket, eHTTPD *parent, int persistent): eSocket(socket, issocket, parent->ml), parent(parent), persistent(persistent)
56 {
57 #ifdef DEBUG_HTTPD
58         eDebug("eHTTPConnection");
59 #endif
60         CONNECT(this->readyRead_ , eHTTPConnection::readData);
61         CONNECT(this->bytesWritten_ , eHTTPConnection::bytesWritten);
62         CONNECT(this->error_ , eHTTPConnection::gotError);
63         CONNECT(this->connectionClosed_ , eHTTPConnection::destruct);
64         CONNECT(this->hangup , eHTTPConnection::gotHangup);
65
66         buffersize=128*1024;
67         localstate=stateWait;
68         remotestate=stateRequest;
69         data=0;
70 }
71
72 void eHTTPConnection::destruct()
73 {
74         gotHangup();
75         delete this;
76 }
77
78 eHTTPConnection::eHTTPConnection(eMainloop *ml): eSocket(ml), parent(0), persistent(0)
79 {
80         CONNECT(this->readyRead_ , eHTTPConnection::readData);
81         CONNECT(this->bytesWritten_ , eHTTPConnection::bytesWritten);
82         CONNECT(this->error_ , eHTTPConnection::gotError);
83         CONNECT(this->connected_ , eHTTPConnection::hostConnected);     
84         CONNECT(this->connectionClosed_ , eHTTPConnection::destruct);
85
86         localstate=stateWait;
87         remotestate=stateWait;
88         
89         buffersize=64*1024;
90         data=0;
91 }
92
93 void eHTTPConnection::hostConnected()
94 {
95         processLocalState();
96 }
97
98 void eHTTPConnection::start()
99 {
100         if (localstate==stateWait)
101         {
102                 localstate=stateRequest;
103                 processLocalState();
104         }
105 }
106
107 void eHTTPConnection::gotHangup()
108 {
109         if (data && remotestate == stateData)
110                 data->haveData(0, 0);
111         if (data)
112         {
113                 delete data;
114                 data=0;
115         }
116         transferDone(0);
117
118         localstate=stateWait;
119         remotestate=stateRequest;
120         
121         remote_header.clear();
122         local_header.clear();
123 }
124
125 eHTTPConnection *eHTTPConnection::doRequest(const char *uri, eMainloop *ml, int *error)
126 {
127         if (error)
128                 *error=0;
129
130         char *defaultproto="http";
131         std::string proto, host, path;
132         int port=80;
133         
134         int state=0; // 0 proto, 1 host, 2 port 3 path
135         
136         while (*uri)
137         {
138                 switch (state)
139                 {
140                 case 0:
141                         if (!strncmp(uri, "://", 3))
142                         {
143                                 state=1;
144                                 uri+=3;
145                         } else if ((*uri=='/') || (*uri==':'))
146                         {
147                                 host=proto;
148                                 state=1;
149                                 proto=defaultproto;
150                         } else
151                                 proto.push_back(*uri++);
152                         break;
153                 case 1:
154                         if (*uri=='/')
155                                 state=3;
156                         else if (*uri==':')
157                         {
158                                 state=2;
159                                 port=0;
160                                 uri++;
161                         } else
162                                 host.push_back(*uri++);
163                         break;
164                 case 2:
165                         if (*uri=='/')
166                                 state=3;
167                         else
168                         {
169                                 if (!isdigit(*uri))
170                                 {
171                                         port=-1;
172                                         state=3;
173                                 } else
174                                 {
175                                         port*=10;
176                                         port+=*uri++-'0';
177                                 }
178                         }
179                         break;
180                 case 3:
181                         path.push_back(*uri++);
182                 }
183         }
184         
185         if (state==0)
186         {
187                 path=proto;
188                 proto=defaultproto;
189         }
190
191 #ifdef DEBUG_HTTPD
192         eDebug("proto: '%s', host '%s', path '%s', port '%d'", proto.c_str(), host.c_str(), path.c_str(), port);
193 #endif
194
195         if (!host.size())
196         {
197                 eDebug("no host given");
198                 if (error)
199                         *error=ENOENT;
200                 return 0;
201         }
202         
203         if (strcmp(proto.c_str(), "http"))
204         {
205                 eDebug("invalid protocol (%s)", proto.c_str());
206                 if (error)
207                         *error=EINVAL;
208                 return 0;
209         }
210         
211         if (port == -1)
212         {
213                 eDebug("invalid port");
214                 if (error)
215                         *error=EINVAL;
216                 return 0;
217         }
218         
219         if (!path.size())
220                 path="/";
221
222         eHTTPConnection *c=new eHTTPConnection(ml);
223         c->request="GET";
224         c->requestpath=path.c_str();
225         c->httpversion="HTTP/1.0";
226         c->local_header["Host"]=host;
227         if ((*error=c->connectToHost(host, port))) // already deleted by error
228                 return 0;
229         return c;
230 }
231
232 void eHTTPConnection::readData()
233 {
234         processRemoteState();
235 }
236
237 void eHTTPConnection::bytesWritten(int)
238 {
239         processLocalState();
240 }
241
242 int eHTTPConnection::processLocalState()
243 {
244         switch (state())
245         {
246         case Connection:
247                 break;
248         default:
249                 return 0;
250         }
251         int done=0;
252         while (!done)
253         {
254 #ifdef DEBUG_HTTPD
255                 eDebug("processing local state %d", localstate);
256 #endif
257                 switch (localstate)
258                 {
259                 case stateWait:
260 #ifdef DEBUG_HTTPD
261                         eDebug("local wait");
262 #endif
263                         done=1;
264                         break;
265                 case stateRequest:
266                 {
267 #ifdef DEBUG_HTTPD
268                         eDebug("local request");
269 #endif
270                         std::string req=request+" "+requestpath+" "+httpversion+"\r\n";
271                         writeBlock(req.c_str(), req.length());
272                         localstate=stateHeader;
273                         remotestate=stateResponse;
274                         break;
275                 }
276                 case stateResponse:
277                 {
278 #ifdef DEBUG_HTTPD
279                         eDebug("local Response");
280 #endif
281                         writeString( (httpversion + " " + getNum(code) + " " + code_descr + "\r\n").c_str() );
282                         localstate=stateHeader;
283                         local_header["Connection"]="close";
284                         break;
285                 }
286                 case stateHeader:
287 #ifdef DEBUG_HTTPD
288                         eDebug("local header");
289 #endif
290                         for (std::map<std::string,std::string>::iterator cur=local_header.begin(); cur!=local_header.end(); ++cur)
291                         {
292                                 writeString(cur->first.c_str());
293                                 writeString(": ");
294                                 writeString(cur->second.c_str());
295                                 writeString("\r\n");
296                         }
297                         writeString("\r\n");
298                         if (request=="HEAD")
299                                 localstate=stateDone;
300                         else
301                                 localstate=stateData;
302                         break;
303                 case stateData:
304 #ifdef DEBUG_HTTPD
305                         eDebug("local data");
306 #endif
307                         if (data)
308                         {
309                                 int btw=buffersize-bytesToWrite();
310                                 if (btw>0)
311                                 {
312                                         if (data->doWrite(btw)<0)
313                                         {
314                                                 localstate=stateDone;
315                                         } else
316                                                 done=1;
317                                 } else
318                                         done=1;
319                         } else
320                                 done=1; // wait for remote response
321                         break;
322                 case stateDone:
323 #if 0
324                         // move to stateClose
325                         if (remote_header.find("Connection") != remote_header.end())
326                         {
327                                 std::string &connection=remote_header["Connection"];
328                                 if (connection == "keep-alive")
329                                         localstate=stateWait;
330                                 else
331                                         localstate=stateClose;
332                         }
333 #endif
334 #ifdef DEBUG_HTTPD
335                         eDebug("locate state done");
336 #endif
337                         if (!persistent)
338                                 localstate=stateClose;
339                         else
340                                 localstate=stateWait;
341                         break;
342                 case stateClose:
343 #ifdef DEBUG_HTTPD
344                         eDebug("closedown");
345 #endif
346                         if (persistent)
347                         {
348                                 if (data)
349                                         delete data;
350                                 data=0;
351                                 localstate=stateWait;
352                         } else
353                                 close();                // bye, bye, remote
354                         return 1;
355                 }
356         }
357 #ifdef DEBUG_HTTPD
358         eDebug("end local");
359 #endif
360         return 0;
361 }
362
363 int eHTTPConnection::processRemoteState()
364 {
365         int abort=0, done=0;
366 #ifdef DEBUG_HTTPD
367         eDebug("%d bytes avail", bytesAvailable());
368 #endif
369         while (((!done) || bytesAvailable()) && !abort)
370         {
371                 switch (remotestate)
372                 {
373                 case stateWait:
374                 {
375                         int i=0;
376 #ifdef DEBUG_HTTPD
377                         eDebug("remote stateWait");
378 #endif
379                         char buffer[1024];
380                         while (bytesAvailable()) {
381                                 i=readBlock(buffer, 1024);
382                         }
383                         done=1;
384                         break;
385                 }
386                 case stateRequest:
387                 {
388 #ifdef DEBUG_HTTPD
389                         eDebug("stateRequest");
390 #endif
391                         std::string line;
392                         if (!getLine(line))
393                         {
394                                 done=1;
395                                 abort=1;
396                                 break;
397                         }
398         
399                         int del[2];
400                         del[0]=line.find(" ");
401                         del[1]=line.find(" ", del[0]+1);
402                         if (del[0]==-1)
403                         {
404                                 if (data)
405                                         delete data;
406                                 eDebug("request buggy");
407                                 httpversion="HTTP/1.0";
408                                 data=new eHTTPError(this, 400);
409                                 done=0;
410                                 localstate=stateResponse;
411                                 remotestate=stateDone;
412                                 if (processLocalState())
413                                         return -1;
414                                 break;
415                         }
416                         request=line.substr(0, del[0]);
417                         requestpath=line.substr(del[0]+1, (del[1]==-1)?-1:(del[1]-del[0]-1));
418                         if (del[1]!=-1)
419                         {
420                                 is09=0;
421                                 httpversion=line.substr(del[1]+1);
422                         } else
423                                 is09=1;
424
425                         if (is09 || (httpversion.substr(0, 7) != "HTTP/1.") || httpversion.size()!=8)
426                         {
427                                 remotestate=stateData;
428                                 done=0;
429                                 httpversion="HTTP/1.0";
430                                 content_length_remaining=content_length_remaining=0;
431                                 data=new eHTTPError(this, 400); // bad request - not supporting version 0.9 yet
432                         } else
433                                 remotestate=stateHeader;
434                         break;
435                 }
436                 case stateResponse:
437                 {
438 #ifdef DEBUG_HTTPD
439                         eDebug("state response..");
440 #endif
441                         std::string line;
442                         if (!getLine(line))
443                         {
444                                 done=1;
445                                 abort=1;
446                                 break;
447                         }
448 #ifdef DEBUG_HTTPD
449                         eDebug("line: %s", line.c_str());
450 #endif
451                         int del[2];
452                         del[0]=line.find(" ");
453                         del[1]=line.find(" ", del[0]+1);
454                         if (del[0]==-1)
455                                 code=-1;
456                         else
457                         {
458                                 httpversion=line.substr(0, del[0]);
459                                 code=atoi(line.substr(del[0]+1, (del[1]==-1)?-1:(del[1]-del[0]-1)).c_str());
460                                 if (del[1] != -1)
461                                         code_descr=line.substr(del[1]+1);
462                                 else
463                                         code_descr="";
464                         }
465                         
466                         remotestate=stateHeader;
467                         break;
468                 }
469                 case stateHeader:
470                 {
471 #ifdef DEBUG_HTTPD
472                         eDebug("remote stateHeader");
473 #endif
474                         std::string line;
475                         if (!getLine(line))
476                         {
477                                 done=1;
478                                 abort=1;
479                                 break;
480                         }
481                         if (!line.length())
482                         {
483                                 content_length=0;
484                                 content_length_remaining=-1;
485                                 if (remote_header.count("Content-Length"))
486                                 {
487                                         content_length=atoi(remote_header["Content-Length"].c_str());
488                                         content_length_remaining=content_length;
489                                 }
490
491                                 if (parent)
492                                 {
493                                         for (ePtrList<eHTTPPathResolver>::iterator i(parent->resolver); i != parent->resolver.end(); ++i)
494                                         {
495                                                 if ((data=i->getDataSource(request, requestpath, this)))
496                                                         break;
497                                         }
498                                         localstate=stateResponse;               // can be overridden by dataSource
499                                 } else
500                                         data=createDataSource(this);
501
502                                 if (!data)
503                                 {
504                                         if (data)
505                                                 delete data;
506                                         data=new eHTTPError(this, 404);
507                                 }
508
509                                 if (content_length ||           // if content-length is set, we have content
510                                                 remote_header.count("Content-Type") ||          // content-type - the same
511                                                 (localstate != stateResponse))  // however, if we are NOT in response-state, so we are NOT server, there's ALWAYS more data to come. (exception: http/1.1 persistent)
512                                         remotestate=stateData;
513                                 else
514                                 {
515                                         data->haveData(0, 0);
516                                         remotestate=stateDone;
517                                 }
518                                 if (processLocalState())
519                                         return -1;
520                         } else
521                         {
522                                 int del=line.find(":");
523                                 std::string name=line.substr(0, del), value=line.substr(del+1);
524                                 if (value[0]==' ')
525                                         value=value.substr(1);
526                                 remote_header[std::string(name)]=std::string(value);
527                         }
528                         done=1;
529                         break;
530                 }
531                 case stateData:
532                 {
533 #ifdef DEBUG_HTTPD
534                         eDebug("remote stateData");
535 #endif
536                         ASSERT(data);
537                         char buffer[16284];
538                         int len;
539                         while (bytesAvailable())
540                         {
541                                 int tr=sizeof(buffer);
542                                 if (content_length_remaining != -1)
543                                         if (tr>content_length_remaining)
544                                                 tr=content_length_remaining;
545                                 len=readBlock(buffer, tr);
546                                 data->haveData(buffer, len);
547                                 if (content_length_remaining != -1)
548                                         content_length_remaining-=len;
549                                 if (!content_length_remaining)
550                                 {
551                                         data->haveData(0, 0);
552                                         remotestate=stateDone;
553                                         break;
554                                 }
555                         }
556                         done=1;
557                         if (processLocalState())
558                                 return -1;
559                         break;
560                 }
561                 case stateDone:
562                         remotestate=stateClose;
563                         break;
564                 case stateClose:
565 //                      if (!persistent)
566                                 remotestate=stateWait;
567 //                      else
568 //                              remotestate=stateRequest;
569                         abort=1;
570                         break;
571                 default:
572                         eDebug("HTTP: invalid state %d", remotestate);
573                         done=1;
574                 }
575         }
576 #ifdef DEBUG_HTTPD
577         eDebug("end remote");
578 #endif
579         return 0;
580 }
581
582 void eHTTPConnection::writeString(const char *data)
583 {
584         writeBlock(data, strlen(data));
585 }
586
587 int eHTTPConnection::getLine(std::string &line)
588 {
589         if (!canReadLine())
590                 return 0;
591
592         line = readLine();
593         line.erase(line.length()-1);
594
595         if (line[(line.length()-1)] == '\r')
596                 line.erase(line.length()-1);
597         
598         return 1;
599 }
600
601 void eHTTPConnection::gotError(int err)
602 {
603         if (data)
604         {
605                 delete data;
606                 data=0;
607         }
608         transferDone(err);
609         delete this;
610 }
611
612 eHTTPD::eHTTPD(int port, eMainloop *ml): eServerSocket(port, ml), ml(ml)
613 {
614         if (!ok())
615                 eDebug("[NET] httpd server FAILED on port %d", port);
616         else
617                 eDebug("[NET] httpd server started on port %d", port);
618 #warning resolver autodelete removed
619 }
620
621 eHTTPConnection::~eHTTPConnection()
622 {
623         if ((!persistent) && (state()!=Idle))
624                 eWarning("~eHTTPConnection, status still %d", state());
625         if (data)
626                 delete data;
627 }
628
629 void eHTTPD::newConnection(int socket)
630 {
631         new eHTTPConnection(socket, 1, this);
632 }