better solution to add possibility to delete eSocketNotifiers,
[enigma2.git] / lib / network / http_file.cpp
1 #include <lib/network/http_file.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <errno.h>
5 #include <string>
6 #include <shadow.h>
7 #include <pwd.h>
8
9 DEFINE_REF(eHTTPFile);
10
11 eHTTPFile::eHTTPFile(eHTTPConnection *c, int _fd, int method, const char *mime): eHTTPDataSource(c), method(method)
12 {
13         fd=_fd;
14         if (method == methodGET)
15         {
16                 c->local_header["Content-Type"]=std::string(mime);
17                 size=lseek(fd, 0, SEEK_END);
18                 char asize[10];
19                 snprintf(asize, 10, "%d", size);
20                 lseek(fd, 0, SEEK_SET);
21                 c->local_header["Content-Length"]=std::string(asize);
22         }
23         connection->code_descr="OK";
24         connection->code=200;
25 }
26
27 int eHTTPFile::doWrite(int bytes)
28 {
29         if (method == methodGET)
30         {
31                 char buff[bytes];
32                 if (!size)
33                         return -1;
34                 int len=bytes;
35                 if (len>size)
36                         len=size;
37                 len=read(fd, buff, len);
38                 if (len<=0)
39                         return -1;
40                 size-=connection->writeBlock(buff, len);
41                 if (!size)
42                         return -1;
43                 else
44                         return 1;
45         } else
46                 return -1;
47 }
48
49 void eHTTPFile::haveData(void *data, int len)
50 {
51         if (method != methodPUT)
52                 return;
53         ::write(fd, data, len);
54 }
55
56 eHTTPFile::~eHTTPFile()
57 {
58         close(fd);
59 }
60
61 eHTTPFilePathResolver::eHTTPFilePathResolver()
62 {
63 }
64
65
66 static char _base64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
67
68 static int unbase64(std::string &dst, const std::string string)
69 {
70         dst="";
71         char c[4];
72         int pos=0;
73         unsigned int i=0;
74         
75         while (1)
76         {
77                 if (i == string.size())
78                         break;
79                 char *ch=strchr(_base64, string[i++]);
80                 if (!ch)
81                 {
82                         i++;
83                         continue;
84                 }
85                 c[pos++]=ch-_base64;
86                 if (pos == 4)
87                 {
88                         char d[3];
89                         d[0]=c[0]<<2;
90                         d[0]|=c[1]>>4;
91                         d[1]=c[1]<<4;
92                         d[1]|=c[2]>>2;
93                         d[2]=c[2]<<6;
94                         d[2]|=c[3];
95                         
96                         dst+=d[0];
97                         if (c[2] != 64)
98                                 dst+=d[1];
99                         if (c[3] != 64)
100                                 dst+=d[2];
101                         pos=0;
102                 }
103         }
104         return pos;
105 }
106
107 int CheckUnixPassword(const char *user, const char *pass)
108 {
109         passwd *pwd=getpwnam(user);
110         if (!pwd)
111                 return -1;
112         char *cpwd=pwd->pw_passwd;
113         if (pwd && (!strcmp(pwd->pw_passwd, "x")))
114         {
115                 spwd *sp=getspnam(user);
116                 if (!sp)                                                // no shadow password defined.
117                         return -1;
118                 cpwd=sp->sp_pwdp;
119         }
120         if (!cpwd)
121                 return -1;
122         if ((*cpwd=='!')||(*cpwd=='*'))          // disabled user
123                 return -2;
124         char *cres=crypt(pass, cpwd);
125         return !!strcmp(cres, cpwd);
126 }
127
128 static int checkAuth(const std::string cauth)
129 {
130         std::string auth;
131         if (cauth.substr(0, 6) != "Basic ")
132                 return -1;
133         if (unbase64(auth, cauth.substr(6)))
134                 return -1;
135         std::string username=auth.substr(0, auth.find(":"));
136         std::string password=auth.substr(auth.find(":")+1);
137         if (CheckUnixPassword(username.c_str(), password.c_str()))
138                 return -1;
139         return 0;
140 }
141
142 DEFINE_REF(eHTTPFilePathResolver);
143
144 RESULT eHTTPFilePathResolver::getDataSource(eHTTPDataSourcePtr &ptr, std::string request, std::string path, eHTTPConnection *conn)
145 {
146         int method;
147         eDebug("request = %s, path = %s", request.c_str(), path.c_str());
148         if (request == "GET")
149                 method=eHTTPFile::methodGET;
150         else if (request == "PUT")
151                 method=eHTTPFile::methodPUT;
152         else
153         {
154                 ptr = new eHTTPError(conn, 405); // method not allowed
155                 return 0;
156         }
157         if (path.find("../")!=std::string::npos)                // evil hax0r
158         {
159                 ptr = new eHTTPError(conn, 403);
160                 return 0;
161         }
162         if (path[0] != '/')             // prepend '/'
163                 path.insert(0,"/");
164         if (path[path.length()-1]=='/')
165                 path+="index.html";
166         
167         eHTTPDataSource *data=0;
168         for (ePtrList<eHTTPFilePath>::iterator i(translate); i != translate.end(); ++i)
169         {
170                 if (i->root==path.substr(0, i->root.length()))
171                 {
172                         std::string newpath=i->path+path.substr(i->root.length());
173                         if (newpath.find('?'))
174                                 newpath=newpath.substr(0, newpath.find('?'));
175                         eDebug("translated %s to %s", path.c_str(), newpath.c_str());
176
177                         if (i->authorized & ((method==eHTTPFile::methodGET)?1:2))
178                         {
179                                 std::map<std::string, std::string>::iterator i=conn->remote_header.find("Authorization");
180                                 if ((i == conn->remote_header.end()) || checkAuth(i->second))
181                                 {
182                                         conn->local_header["WWW-Authenticate"]="Basic realm=\"dreambox\"";
183                                         ptr = new eHTTPError(conn, 401); // auth req'ed
184                                         return 0;
185                                 }
186                         }
187
188                         int fd=open(newpath.c_str(), (method==eHTTPFile::methodGET)?O_RDONLY:(O_WRONLY|O_CREAT|O_TRUNC), 0644);
189
190                         if (fd==-1)
191                         {
192                                 switch (errno)
193                                 {
194                                 case ENOENT:
195                                         data=new eHTTPError(conn, 404);
196                                         break;
197                                 case EACCES:
198                                         data=new eHTTPError(conn, 403);
199                                         break;
200                                 default:
201                                         data=new eHTTPError(conn, 403); // k.a.
202                                         break;
203                                 }
204                                 break;
205                         }
206                         
207                         std::string ext=path.substr(path.rfind('.'));
208                         const char *mime="text/unknown";
209                         if ((ext==".html") || (ext==".htm"))
210                                 mime="text/html";
211                         else if ((ext==".jpeg") || (ext==".jpg"))
212                                 mime="image/jpeg";
213                         else if (ext==".gif")
214                                 mime="image/gif";
215                         else if (ext==".css")
216                                 mime="text/css";
217                         else if (ext==".png")
218                                 mime="image/png";
219                         else if (ext==".xml")
220                                 mime="text/xml";
221                         else if (ext==".xsl")
222                                 mime="text/xsl";
223
224                         data=new eHTTPFile(conn, fd, method, mime);
225                         break;
226                 }
227         }
228         if (!data)
229                 return -1;
230         ptr = data;
231         return 0;
232 }
233
234 void eHTTPFilePathResolver::addTranslation(std::string path, std::string root, int authorized)
235 {
236         if (path[path.length()-1]!='/')
237                 path+='/';
238         if (root[root.length()-1]!='/')
239                 root+='/';
240         translate.push_back(new eHTTPFilePath(path, root, authorized));
241 }