version 0.1.10
[fms.git] / src / http / ipagehandler.cpp
1 #include "../../include/http/ipagehandler.h"\r
2 #include "../../include/http/httpdefs.h"\r
3 #include "../../include/stringfunctions.h"\r
4 \r
5 #ifdef XMEM\r
6         #include <xmem.h>\r
7 #endif\r
8 \r
9 void IPageHandler::CreateArgArray(const std::map<std::string,std::string> &vars, const std::string &basename, std::vector<std::string> &args)\r
10 {\r
11         for(std::map<std::string,std::string>::const_iterator i=vars.begin(); i!=vars.end(); i++)\r
12         {\r
13                 if((*i).first.find(basename)==0 && (*i).first.find("[")!=std::string::npos && (*i).first.find("]")!=std::string::npos)\r
14                 {\r
15                         int index;\r
16                         std::string indexstr;\r
17                         std::string::size_type startpos;\r
18                         std::string::size_type endpos;\r
19                         startpos=(*i).first.find("[");\r
20                         endpos=(*i).first.find("]");\r
21 \r
22                         indexstr=(*i).first.substr(startpos+1,(endpos-startpos)-1);\r
23                         StringFunctions::Convert(indexstr,index);\r
24 \r
25                         while(args.size()<index+1)\r
26                         {\r
27                                 args.push_back("");\r
28                         }\r
29                         args[index]=(*i).second;\r
30                 }\r
31         }\r
32 }\r
33 \r
34 const bool IPageHandler::Handle(shttpd_arg *arg)\r
35 {\r
36         const char *uri=shttpd_get_env(arg,"REQUEST_URI");\r
37         const char *method=shttpd_get_env(arg,"REQUEST_METHOD");\r
38         std::string methodstr="";\r
39         if(method)\r
40         {\r
41                 methodstr=method;\r
42         }\r
43 \r
44         if(uri && WillHandleURI(std::string(uri)))\r
45         {\r
46                 httpstate *mystate=(httpstate *)arg->state;\r
47 \r
48                 // first check if there was a connection error - if so delete the input/output and state buffers and return immediately\r
49                 if((arg->flags & SHTTPD_CONNECTION_ERROR)==SHTTPD_CONNECTION_ERROR)\r
50                 {\r
51                         if(mystate && mystate->m_indata)\r
52                         {\r
53                                 delete mystate->m_indata;\r
54                                 mystate->m_indata=NULL;\r
55                         }\r
56                         if(mystate && mystate->m_outdata)\r
57                         {\r
58                                 delete mystate->m_outdata;\r
59                                 mystate->m_outdata=NULL;\r
60                         }\r
61                         if(mystate)\r
62                         {\r
63                                 delete mystate;\r
64                                 mystate=NULL;\r
65                         }\r
66                         return true;\r
67                 }\r
68 \r
69                 // this is a new request - create a new arg object\r
70                 if(arg->state==NULL)\r
71                 {\r
72                         arg->state=new httpstate;\r
73                         memset(arg->state,0,sizeof(httpstate));\r
74                         mystate=(httpstate *)arg->state;\r
75 \r
76                         // if post then create input buffer\r
77                         if(methodstr=="POST")\r
78                         {\r
79                                 const char *lenstr=shttpd_get_header(arg,"Content-Length");\r
80                                 if(lenstr)\r
81                                 {\r
82                                         long len;\r
83                                         StringFunctions::Convert(std::string(lenstr),len);\r
84                                         mystate->m_indata=new char[len+1];\r
85                                         mystate->m_indata[len]=NULL;\r
86                                         mystate->m_indatalen=len;\r
87                                         mystate->m_indatapos=0;\r
88                                 }\r
89                         }\r
90                 }\r
91 \r
92                 // we have more POST data to get\r
93                 if(arg->in.len>0)\r
94                 {\r
95                         int pos=0;\r
96                         while(pos<arg->in.len)\r
97                         {\r
98                                 mystate->m_indata[mystate->m_indatapos++]=arg->in.buf[pos++];\r
99                         }\r
100                         arg->in.num_bytes=arg->in.len;\r
101                 }\r
102 \r
103                 // we have all POST data (or it was 0 to begin with) - generate the page\r
104                 if(mystate->m_indatalen==mystate->m_indatapos && mystate->m_outdata==NULL)\r
105                 {\r
106                         //parse POST data and any QUERY_STRING before generating page\r
107                         std::map<std::string,std::string> args;\r
108                         std::vector<std::string> argparts;\r
109                         std::string contenttype="";\r
110                         \r
111                         if(shttpd_get_header(arg,"Content-Type"))\r
112                         {\r
113                                 contenttype=shttpd_get_header(arg,"Content-Type");\r
114                         }\r
115                         \r
116                         if(contenttype.find("multipart/form-data")!=std::string::npos)\r
117                         {\r
118                                 HandleMultiPartData(contenttype,mystate->m_indata,mystate->m_indatalen,args);\r
119                         }\r
120                         else\r
121                         {\r
122                                 // split apart non-multipart POST\r
123                                 if(mystate->m_indata)\r
124                                 {\r
125                                         StringFunctions::Split(mystate->m_indata,"&",argparts);\r
126                                 }\r
127                                 // split apart query string\r
128                                 if(shttpd_get_env(arg,"QUERY_STRING"))\r
129                                 {\r
130                                         StringFunctions::Split(shttpd_get_env(arg,"QUERY_STRING"),"&",argparts);\r
131                                 }\r
132 \r
133                                 for(std::vector<std::string>::iterator argi=argparts.begin(); argi!=argparts.end(); argi++)\r
134                                 {\r
135                                         std::vector<std::string> parts;\r
136                                         StringFunctions::Split((*argi),"=",parts);\r
137                                         if(parts.size()>0)\r
138                                         {\r
139                                                 // replace + with space before UriDecoding\r
140                                                 parts[0]=StringFunctions::Replace(parts[0],"+"," ");\r
141                                                 args[StringFunctions::UriDecode(parts[0])];\r
142                                                 if(parts.size()>1)\r
143                                                 {\r
144                                                         // replace + with space before UriDecoding\r
145                                                         parts[1]=StringFunctions::Replace(parts[1],"+"," ");\r
146                                                         args[StringFunctions::UriDecode(parts[0])]=StringFunctions::UriDecode(parts[1]);\r
147                                                 }\r
148                                         }\r
149                                 }\r
150                         }\r
151 \r
152                         std::string page=GeneratePage(methodstr,args);\r
153                         mystate->m_outdata=new char[page.size()];\r
154                         memcpy(mystate->m_outdata,page.c_str(),page.size());\r
155                         mystate->m_outdatalen=page.size();\r
156                         mystate->m_outdatapos=0;\r
157                 }\r
158 \r
159                 // if we have output data, push next block of data onto out buffer\r
160                 if(mystate->m_outdata && mystate->m_outdatapos<mystate->m_outdatalen)\r
161                 {\r
162                         int pos=0;\r
163                         while(mystate->m_outdatapos<mystate->m_outdatalen && pos<arg->out.len)\r
164                         {\r
165                                 arg->out.buf[pos++]=mystate->m_outdata[mystate->m_outdatapos++];\r
166                         }\r
167                         arg->out.num_bytes=pos;\r
168                 }\r
169 \r
170                 // if we have no more output data to send - delete the data pointers and set end of output flag\r
171                 if(mystate->m_outdata && mystate->m_outdatapos==mystate->m_outdatalen)\r
172                 {\r
173                         if(mystate->m_indata)\r
174                         {\r
175                                 delete [] mystate->m_indata;\r
176                         }\r
177                         if(mystate->m_outdata)\r
178                         {\r
179                                 delete [] mystate->m_outdata;\r
180                         }\r
181                         delete mystate;\r
182                         arg->state=NULL;\r
183 \r
184                         arg->flags|=SHTTPD_END_OF_OUTPUT;\r
185                 }\r
186 \r
187                 return true;\r
188         }\r
189         else\r
190         {\r
191                 return false;\r
192         }\r
193 }\r
194 \r
195 void IPageHandler::HandleMultiPartData(const std::string &contenttypeheader, char *data, const long datalen, std::map<std::string,std::string> &args)\r
196 {\r
197         if(data)\r
198         {\r
199                 std::string datastr(data,data+datalen);\r
200                 std::vector<std::string> parts;\r
201                 std::string boundary="";\r
202                 std::string::size_type pos=contenttypeheader.find("boundary=");\r
203 \r
204                 // find boundary\r
205                 if(pos!=std::string::npos)\r
206                 {\r
207                         boundary=contenttypeheader.substr(pos+9);\r
208                         // strip off any " and ;\r
209                         boundary=StringFunctions::Replace(boundary,"\"","");\r
210                         boundary=StringFunctions::Replace(boundary,";","");\r
211                 }\r
212 \r
213                 // split into parts separated by boundary\r
214                 StringFunctions::Split(datastr,"--"+boundary+"\r\n",parts);\r
215 \r
216                 // go through each part and get name=value\r
217                 for(std::vector<std::string>::iterator i=parts.begin(); i!=parts.end(); i++)\r
218                 {\r
219                         std::string data="";\r
220                         std::string name="";\r
221 \r
222                         // find name\r
223                         pos=(*i).find("name=");\r
224                         if(pos!=std::string::npos)\r
225                         {\r
226                                 std::string::size_type pos2=(*i).find(";",pos);\r
227                                 if(pos2!=std::string::npos)\r
228                                 {\r
229                                         name=(*i).substr(pos+5,pos2-(pos+5));\r
230                                 }\r
231                                 else\r
232                                 {\r
233                                         pos2=(*i).find("\r\n",pos);\r
234                                         if(pos2!=std::string::npos)\r
235                                         {\r
236                                                 name=(*i).substr(pos+5,pos2-(pos+5));\r
237                                         }\r
238                                 }\r
239 \r
240                                 name=StringFunctions::Replace(name,"\"","");\r
241                         }\r
242 \r
243                         // find header boundary\r
244                         pos=(*i).find("\r\n\r\n");\r
245                         if(pos!=std::string::npos)\r
246                         {\r
247                                 data=(*i).substr(pos+4);\r
248                                 // strip off final \r\n from data\r
249                                 if(data.size()>2 && data.rfind("\r\n")==data.size()-2)\r
250                                 {\r
251                                         data.erase(data.size()-2);\r
252                                 }\r
253                         }\r
254                         if(name!="" && data!="")\r
255                         {\r
256                                 args[name]=data;\r
257                         }\r
258                 }\r
259         }\r
260 }\r
261 \r
262 const std::string IPageHandler::SanitizeOutput(const std::string &input)\r
263 {\r
264         // must do & first because all other elements have & in them!\r
265         std::string output=StringFunctions::Replace(input,"&","&amp;");\r
266         output=StringFunctions::Replace(output,"<","&lt;");\r
267         output=StringFunctions::Replace(output,">","&gt;");\r
268         output=StringFunctions::Replace(output,"\"","&quot;");\r
269         output=StringFunctions::Replace(output," ","&nbsp;");\r
270         return output;\r
271 }\r