1 /** \file HttpdForm.cpp - read stdin, parse cgi input
3 ** Written: 1999-Feb-10 grymse@alhem.net
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.
32 #pragma warning(disable:4786)
35 #include "socket_include.h"
38 #include "HttpdForm.h"
40 #ifdef SOCKETS_NAMESPACE
41 namespace SOCKETS_NAMESPACE
{
47 HttpdForm::HttpdForm(IFile
*infil
, const std::string
& content_type
, size_t content_length
) : raw(false)
53 m_current
= m_cgi
.end();
56 if (content_type
.size() >= 19 && content_type
.substr(0, 19) == "multipart/form-data")
58 Parse
pa(content_type
,";=");
62 std::string str
= pa
.getword();
66 if (!strcmp(str
.c_str(),"boundary"))
68 m_strBoundary
= pa
.getword();
69 l
= m_strBoundary
.size();
70 tempcmp
= new char[l
+ extra
];
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;
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();
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
)
109 std::string h
= pa
.getword();
110 if (!strcasecmp(h
.c_str(),"Content-type:"))
112 content_type
= pa
.getword();
115 if (!strcasecmp(h
.c_str(),"Content-Disposition:"))
118 if (!strcmp(h
.c_str(),"form-data"))
120 pa
.EnableQuote(true);
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);
139 if (!strcmp(name
.c_str(),"filename"))
141 if (h
.size() && h
[0] == '"')
143 current_filename
= h
.substr(1, h
.size() - 2);
147 current_filename
= h
;
150 for (size_t i
= 0; i
< current_filename
.size(); i
++)
152 if (current_filename
[i
] == '/' || current_filename
[i
] == '\\')
157 current_filename
= current_filename
.substr(x
);
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
175 infil
-> fgets(slask
, TMPSIZE
);
176 while (!infil
-> eof() && strncmp(slask
,m_strBoundary
.c_str(),m_strBoundary
.size() ))
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...
195 char fn
[2000]; // where post'd file will be saved
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());
207 sprintf(fn
,"/tmp/%s",current_filename
.c_str());
209 if ((fil
= fopen(fn
, "wb")) != NULL
)
211 infil
-> fread(&c
,1,1);
212 while (!infil
-> eof())
216 fwrite(&tempcmp
[tc
],1,1,fil
);
227 if (!strncmp(tempcmp
+ tc
+ extra
, m_strBoundary
.c_str(), l
- tc
) &&
228 !strncmp(tempcmp
, m_strBoundary
.c_str() + l
- tc
, tc
))
235 if (!strncmp(tempcmp
+ extra
, m_strBoundary
.c_str(), l
))
240 infil
-> fread(&c
,1,1);
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
252 // couldn't open file
259 // Probably '<m_strBoundary>--'
262 } // while (!infil -> eof())
264 } // if (m_strBoundary)
271 if (strstr(content_type
.c_str(), "x-www-form-urlencoded"))
273 bool got_name
= false; // tnx to FatherNitwit
275 int cl
= (int)content_length
;
277 char *slask
= new char[TMPSIZE
];
278 m_current
= m_cgi
.end();
282 infil
-> fread(&c
,1,1);
284 while (cl
>= 0 && !infil
-> eof())
288 case '=': /* end of name */
294 case '&': /* end of value */
299 cgi
= new CGI(name
,slask
);
304 cgi
= new CGI(slask
, "");
306 m_cgi
.push_back(cgi
);
308 case '+': /* space */
311 case '%': /* hex value */
312 infil
-> fread(&chigh
,1,1);
314 chigh
-= 48 + (chigh
> '9' ? 7 : 0) + (chigh
>= 'a' ? 32 : 0);
315 infil
-> fread(&clow
,1,1);
317 clow
-= 48 + (clow
> '9' ? 7 : 0) + (clow
>= 'a' ? 32 : 0);
318 slask
[i
++] = (char)(chigh
* 16 + clow
);
320 default: /* just another char */
327 infil
-> fread(&c
,1,1);
335 cgi
= new CGI(name
,slask
);
339 cgi
= new CGI(slask
, "");
341 m_cgi
.push_back(cgi
);
347 // HttpdForm(buffer,l) -- request_method GET
349 HttpdForm::HttpdForm(const std::string
& buffer
,size_t l
) : raw(false)
352 char *slask
= new char[TMPSIZE
];
353 char *name
= new char[TMPSIZE
];
357 bool got_name
= false;
359 m_current
= m_cgi
.end();
369 case '=': /* end of name */
375 case '&': /* end of value */
380 cgi
= new CGI(name
,slask
);
385 cgi
= new CGI(slask
, "");
387 m_cgi
.push_back(cgi
);
389 case '+': /* space */
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
);
399 default: /* just another char */
408 cgi
= new CGI(name
,slask
);
412 cgi
= new CGI(slask
, "");
414 m_cgi
.push_back(cgi
);
420 HttpdForm::~HttpdForm()
422 CGI
*cgi
= NULL
; //,*tmp;
424 for (cgi_v::iterator it
= m_cgi
.begin(); it
!= m_cgi
.end(); it
++)
432 void HttpdForm::EnableRaw(bool b
)
438 void HttpdForm::strcpyval(std::string
& v
,const char *value
) const
441 for (size_t i
= 0; i
< strlen(value
); i
++)
465 bool HttpdForm::getfirst(std::string
& n
) const
467 m_current
= m_cgi
.begin();
472 bool HttpdForm::getnext(std::string
& n
) const
474 if (m_current
!= m_cgi
.end() )
476 CGI
*current
= *m_current
;
489 bool HttpdForm::getfirst(std::string
& n
,std::string
& v
) const
491 m_current
= m_cgi
.begin();
496 bool HttpdForm::getnext(std::string
& n
,std::string
& v
) const
498 if (m_current
!= m_cgi
.end() )
500 CGI
*current
= *m_current
;
504 v
= current
-> value
;
508 strcpyval(v
,current
-> value
.c_str());
521 int HttpdForm::getvalue(const std::string
& n
,std::string
& v
) const
526 for (cgi_v::const_iterator it
= m_cgi
.begin(); it
!= m_cgi
.end(); it
++)
529 if (cgi
-> name
== n
)
541 strcpyval(v
,cgi
-> value
.c_str());
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
++)
559 if (cgi
-> name
== n
)
568 size_t HttpdForm::getlength(const std::string
& n
) const
573 for (cgi_v::const_iterator it
= m_cgi
.begin(); it
!= m_cgi
.end(); it
++)
576 if (cgi
-> name
== n
)
580 l
= cgi
? cgi
-> value
.size() : 0;
583 for (size_t i
= 0; i
< cgi
-> value
.size(); i
++)
585 switch (cgi
-> value
[i
])
601 HttpdForm::cgi_v
& HttpdForm::getbase()
607 const std::string
& HttpdForm::GetBoundary() const
609 return m_strBoundary
;
613 #ifdef SOCKETS_NAMESPACE