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