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