switched from PracticalSocket to libasio
[anytun.git] / src / Sockets / HttpdForm.cpp
blob8eb4da1e9d6ac9e9e62b4177871365c35af405fb
1 /** \file HttpdForm.cpp - read stdin, parse cgi input
2 **
3 ** Written: 1999-Feb-10 grymse@alhem.net
4 **/
6 /*
7 Copyright (C) 1999-2007 Anders Hedstrom
9 This library is made available under the terms of the GNU GPL.
11 If you would like to use this library in a closed-source application,
12 a separate license agreement is available. For information about
13 the closed-source license agreement for the C++ sockets library,
14 please visit http://www.alhem.net/Sockets/license.html and/or
15 email license@alhem.net.
17 This program is free software; you can redistribute it and/or
18 modify it under the terms of the GNU General Public License
19 as published by the Free Software Foundation; either version 2
20 of the License, or (at your option) any later version.
22 This program is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 #ifdef _MSC_VER
32 #pragma warning(disable:4786)
33 #endif
34 #include <cstring>
35 #include "socket_include.h"
36 #include "Parse.h"
37 #include "IFile.h"
38 #include "HttpdForm.h"
40 #ifdef SOCKETS_NAMESPACE
41 namespace SOCKETS_NAMESPACE {
42 #endif
44 #define TMPSIZE 10000
47 HttpdForm::HttpdForm(IFile *infil, const std::string& content_type, size_t content_length) : raw(false)
49 CGI *cgi = NULL;
50 size_t extra = 2;
51 char name[TMPSIZE];
53 m_current = m_cgi.end();
54 *name = 0;
56 if (content_type.size() >= 19 && content_type.substr(0, 19) == "multipart/form-data")
58 Parse pa(content_type,";=");
59 char *tempcmp = NULL;
60 size_t tc = 0;
61 size_t l = 0;
62 std::string str = pa.getword();
63 m_strBoundary = "";
64 while (str.size())
66 if (!strcmp(str.c_str(),"boundary"))
68 m_strBoundary = pa.getword();
69 l = m_strBoundary.size();
70 tempcmp = new char[l + extra];
73 str = pa.getword();
75 if (m_strBoundary.size())
77 std::string content_type;
78 std::string current_name;
79 std::string current_filename;
80 char *slask = new char[TMPSIZE];
81 infil -> fgets(slask, TMPSIZE);
82 while (!infil -> eof())
84 while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10))
86 slask[strlen(slask) - 1] = 0;
88 content_type = "";
89 current_name = "";
90 current_filename = "";
91 if ((strstr(slask,m_strBoundary.c_str()) || strstr(m_strBoundary.c_str(),slask)) && strcmp(slask, m_strBoundary.c_str()))
93 m_strBoundary = slask;
94 l = m_strBoundary.size();
95 delete[] tempcmp;
96 tempcmp = new char[l + extra];
98 if (!strcmp(slask, m_strBoundary.c_str()))
100 // Get headers until empty line
101 infil -> fgets(slask, TMPSIZE);
102 while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10))
104 slask[strlen(slask) - 1] = 0;
106 while (!infil -> eof() && *slask)
108 Parse pa(slask,";");
109 std::string h = pa.getword();
110 if (!strcasecmp(h.c_str(),"Content-type:"))
112 content_type = pa.getword();
114 else
115 if (!strcasecmp(h.c_str(),"Content-Disposition:"))
117 h = pa.getword();
118 if (!strcmp(h.c_str(),"form-data"))
120 pa.EnableQuote(true);
121 h = pa.getword();
122 while (h.size())
124 Parse pa2(slask,"=");
125 std::string name = pa2.getword();
126 std::string h = pa2.getrest();
127 if (!strcmp(name.c_str(),"name"))
129 if (h.size() && h[0] == '"')
131 current_name = h.substr(1, h.size() - 2);
133 else
135 current_name = h;
138 else
139 if (!strcmp(name.c_str(),"filename"))
141 if (h.size() && h[0] == '"')
143 current_filename = h.substr(1, h.size() - 2);
145 else
147 current_filename = h;
149 size_t x = 0;
150 for (size_t i = 0; i < current_filename.size(); i++)
152 if (current_filename[i] == '/' || current_filename[i] == '\\')
153 x = i + 1;
155 if (x)
157 current_filename = current_filename.substr(x);
160 h = pa.getword();
164 // get next header value
165 infil -> fgets(slask, TMPSIZE);
166 while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10))
168 slask[strlen(slask) - 1] = 0;
171 // Read content, save...?
172 if (!current_filename.size()) // not a file
174 std::string val;
175 infil -> fgets(slask, TMPSIZE);
176 while (!infil -> eof() && strncmp(slask,m_strBoundary.c_str(),m_strBoundary.size() ))
178 val += slask;
179 infil -> fgets(slask, TMPSIZE);
181 // remove trailing cr/linefeed
182 while (val.size() && (val[val.size() - 1] == 13 || val[val.size() - 1] == 10))
184 val = val.substr(0,val.size() - 1);
186 cgi = new CGI(current_name, val);
187 m_cgi.push_back(cgi);
189 else // current_filename.size() > 0
191 // read until m_strBoundary...
192 FILE *fil;
193 int out = 0;
194 char c;
195 char fn[2000]; // where post'd file will be saved
196 #ifdef _WIN32
198 char tmp_path[2000];
199 ::GetTempPathA(2000, tmp_path);
200 if (tmp_path[strlen(tmp_path) - 1] != '\\')
202 strcat(tmp_path, "\\");
204 sprintf(fn,"%s%s",tmp_path,current_filename.c_str());
206 #else
207 sprintf(fn,"/tmp/%s",current_filename.c_str());
208 #endif
209 if ((fil = fopen(fn, "wb")) != NULL)
211 infil -> fread(&c,1,1);
212 while (!infil -> eof())
214 if (out)
216 fwrite(&tempcmp[tc],1,1,fil);
218 tempcmp[tc] = c;
219 tc++;
220 if (tc >= l + extra)
222 tc = 0;
223 out = 1;
225 if (tc)
227 if (!strncmp(tempcmp + tc + extra, m_strBoundary.c_str(), l - tc) &&
228 !strncmp(tempcmp, m_strBoundary.c_str() + l - tc, tc))
230 break;
233 else
235 if (!strncmp(tempcmp + extra, m_strBoundary.c_str(), l))
237 break;
240 infil -> fread(&c,1,1);
242 fclose(fil);
244 cgi = new CGI(current_name,fn,fn);
245 m_cgi.push_back(cgi);
247 strcpy(slask, m_strBoundary.c_str());
248 infil -> fgets(slask + strlen(slask), TMPSIZE); // next line
250 else
252 // couldn't open file
253 break;
257 else
259 // Probably '<m_strBoundary>--'
260 break;
262 } // while (!infil -> eof())
263 delete[] slask;
264 } // if (m_strBoundary)
265 if (tempcmp)
267 delete[] tempcmp;
270 else
271 if (strstr(content_type.c_str(), "x-www-form-urlencoded"))
273 bool got_name = false; // tnx to FatherNitwit
274 int i = 0;
275 int cl = (int)content_length;
276 char c,chigh,clow;
277 char *slask = new char[TMPSIZE];
278 m_current = m_cgi.end();
280 *name = 0;
282 infil -> fread(&c,1,1);
283 cl--;
284 while (cl >= 0 && !infil -> eof())
286 switch (c)
288 case '=': /* end of name */
289 slask[i] = 0;
290 i = 0;
291 strcpy(name,slask);
292 got_name = true;
293 break;
294 case '&': /* end of value */
295 slask[i] = 0;
296 i = 0;
297 if (got_name)
299 cgi = new CGI(name,slask);
300 got_name = false;
302 else
304 cgi = new CGI(slask, "");
306 m_cgi.push_back(cgi);
307 break;
308 case '+': /* space */
309 slask[i++] = ' ';
310 break;
311 case '%': /* hex value */
312 infil -> fread(&chigh,1,1);
313 cl--;
314 chigh -= 48 + (chigh > '9' ? 7 : 0) + (chigh >= 'a' ? 32 : 0);
315 infil -> fread(&clow,1,1);
316 cl--;
317 clow -= 48 + (clow > '9' ? 7 : 0) + (clow >= 'a' ? 32 : 0);
318 slask[i++] = (char)(chigh * 16 + clow);
319 break;
320 default: /* just another char */
321 slask[i++] = c;
322 break;
325 if (cl > 0)
327 infil -> fread(&c,1,1);
329 cl--;
331 slask[i] = 0;
332 i = 0;
333 if (got_name)
335 cgi = new CGI(name,slask);
337 else
339 cgi = new CGI(slask, "");
341 m_cgi.push_back(cgi);
342 delete[] slask;
347 // HttpdForm(buffer,l) -- request_method GET
349 HttpdForm::HttpdForm(const std::string& buffer,size_t l) : raw(false)
351 CGI *cgi = NULL;
352 char *slask = new char[TMPSIZE];
353 char *name = new char[TMPSIZE];
354 int i = 0;
355 char c,chigh,clow;
356 size_t ptr = 0;
357 bool got_name = false;
359 m_current = m_cgi.end();
361 *name = 0;
363 ptr = 0;
364 while (ptr < l)
366 c = buffer[ptr++];
367 switch (c)
369 case '=': /* end of name */
370 slask[i] = 0;
371 i = 0;
372 strcpy(name,slask);
373 got_name = true;
374 break;
375 case '&': /* end of value */
376 slask[i] = 0;
377 i = 0;
378 if (got_name)
380 cgi = new CGI(name,slask);
381 got_name = false;
383 else
385 cgi = new CGI(slask, "");
387 m_cgi.push_back(cgi);
388 break;
389 case '+': /* space */
390 slask[i++] = ' ';
391 break;
392 case '%': /* hex value */
393 chigh = buffer[ptr++];
394 chigh -= 48 + (chigh > '9' ? 7 : 0) + (chigh >= 'a' ? 32 : 0);
395 clow = buffer[ptr++];
396 clow -= 48 + (clow > '9' ? 7 : 0) + (clow >= 'a' ? 32 : 0);
397 slask[i++] = (char)(chigh * 16 + clow);
398 break;
399 default: /* just another char */
400 slask[i++] = c;
401 break;
404 slask[i] = 0;
405 i = 0;
406 if (got_name)
408 cgi = new CGI(name,slask);
410 else
412 cgi = new CGI(slask, "");
414 m_cgi.push_back(cgi);
415 delete[] slask;
416 delete[] name;
420 HttpdForm::~HttpdForm()
422 CGI *cgi = NULL; //,*tmp;
424 for (cgi_v::iterator it = m_cgi.begin(); it != m_cgi.end(); it++)
426 cgi = *it;
427 delete cgi;
432 void HttpdForm::EnableRaw(bool b)
434 raw = b;
438 void HttpdForm::strcpyval(std::string& v,const char *value) const
440 v = "";
441 for (size_t i = 0; i < strlen(value); i++)
443 if (value[i] == '<')
445 v += "&lt;";
447 else
448 if (value[i] == '>')
450 v += "&gt;";
452 else
453 if (value[i] == '&')
455 v += "&amp;";
457 else
459 v += value[i];
465 bool HttpdForm::getfirst(std::string& n) const
467 m_current = m_cgi.begin();
468 return getnext(n);
472 bool HttpdForm::getnext(std::string& n) const
474 if (m_current != m_cgi.end() )
476 CGI *current = *m_current;
477 n = current -> name;
478 m_current++;
479 return true;
481 else
483 n = "";
485 return false;
489 bool HttpdForm::getfirst(std::string& n,std::string& v) const
491 m_current = m_cgi.begin();
492 return getnext(n,v);
496 bool HttpdForm::getnext(std::string& n,std::string& v) const
498 if (m_current != m_cgi.end() )
500 CGI *current = *m_current;
501 n = current -> name;
502 if (raw)
504 v = current -> value;
506 else
508 strcpyval(v,current -> value.c_str());
510 m_current++;
511 return true;
513 else
515 n = "";
517 return false;
521 int HttpdForm::getvalue(const std::string& n,std::string& v) const
523 CGI *cgi = NULL;
524 int r = 0;
526 for (cgi_v::const_iterator it = m_cgi.begin(); it != m_cgi.end(); it++)
528 cgi = *it;
529 if (cgi -> name == n)
530 break;
531 cgi = NULL;
533 if (cgi)
535 if (raw)
537 v = cgi -> value;
539 else
541 strcpyval(v,cgi -> value.c_str());
543 r++;
545 else
547 v = "";
550 return r;
554 std::string HttpdForm::getvalue(const std::string& n) const
556 for (cgi_v::const_iterator it = m_cgi.begin(); it != m_cgi.end(); it++)
558 CGI *cgi = *it;
559 if (cgi -> name == n)
561 return cgi -> value;
564 return "";
568 size_t HttpdForm::getlength(const std::string& n) const
570 CGI *cgi = NULL;
571 size_t l;
573 for (cgi_v::const_iterator it = m_cgi.begin(); it != m_cgi.end(); it++)
575 cgi = *it;
576 if (cgi -> name == n)
577 break;
578 cgi = NULL;
580 l = cgi ? cgi -> value.size() : 0;
581 if (cgi && !raw)
583 for (size_t i = 0; i < cgi -> value.size(); i++)
585 switch (cgi -> value[i])
587 case '<': // &lt;
588 case '>': // &gt;
589 l += 4;
590 break;
591 case '&': // &amp;
592 l += 5;
593 break;
597 return l;
601 HttpdForm::cgi_v& HttpdForm::getbase()
603 return m_cgi;
607 const std::string& HttpdForm::GetBoundary() const
609 return m_strBoundary;
613 #ifdef SOCKETS_NAMESPACE
615 #endif