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)
34 #include "socket_include.h"
37 #include "HttpdForm.h"
39 #ifdef SOCKETS_NAMESPACE
40 namespace SOCKETS_NAMESPACE
{
46 HttpdForm::HttpdForm(IFile
*infil
, const std::string
& content_type
, size_t content_length
) : raw(false)
52 m_current
= m_cgi
.end();
55 if (content_type
.size() >= 19 && content_type
.substr(0, 19) == "multipart/form-data")
57 Parse
pa(content_type
,";=");
61 std::string str
= pa
.getword();
65 if (!strcmp(str
.c_str(),"boundary"))
67 m_strBoundary
= pa
.getword();
68 l
= m_strBoundary
.size();
69 tempcmp
= new char[l
+ extra
];
74 if (m_strBoundary
.size())
76 std::string content_type
;
77 std::string current_name
;
78 std::string current_filename
;
79 char *slask
= new char[TMPSIZE
];
80 infil
-> fgets(slask
, TMPSIZE
);
81 while (!infil
-> eof())
83 while (strlen(slask
) && (slask
[strlen(slask
) - 1] == 13 || slask
[strlen(slask
) - 1] == 10))
85 slask
[strlen(slask
) - 1] = 0;
89 current_filename
= "";
90 if ((strstr(slask
,m_strBoundary
.c_str()) || strstr(m_strBoundary
.c_str(),slask
)) && strcmp(slask
, m_strBoundary
.c_str()))
92 m_strBoundary
= slask
;
93 l
= m_strBoundary
.size();
95 tempcmp
= new char[l
+ extra
];
97 if (!strcmp(slask
, m_strBoundary
.c_str()))
99 // Get headers until empty line
100 infil
-> fgets(slask
, TMPSIZE
);
101 while (strlen(slask
) && (slask
[strlen(slask
) - 1] == 13 || slask
[strlen(slask
) - 1] == 10))
103 slask
[strlen(slask
) - 1] = 0;
105 while (!infil
-> eof() && *slask
)
108 std::string h
= pa
.getword();
109 if (!strcasecmp(h
.c_str(),"Content-type:"))
111 content_type
= pa
.getword();
114 if (!strcasecmp(h
.c_str(),"Content-Disposition:"))
117 if (!strcmp(h
.c_str(),"form-data"))
119 pa
.EnableQuote(true);
123 Parse
pa2(slask
,"=");
124 std::string name
= pa2
.getword();
125 std::string h
= pa2
.getrest();
126 if (!strcmp(name
.c_str(),"name"))
128 if (h
.size() && h
[0] == '"')
130 current_name
= h
.substr(1, h
.size() - 2);
138 if (!strcmp(name
.c_str(),"filename"))
140 if (h
.size() && h
[0] == '"')
142 current_filename
= h
.substr(1, h
.size() - 2);
146 current_filename
= h
;
149 for (size_t i
= 0; i
< current_filename
.size(); i
++)
151 if (current_filename
[i
] == '/' || current_filename
[i
] == '\\')
156 current_filename
= current_filename
.substr(x
);
163 // get next header value
164 infil
-> fgets(slask
, TMPSIZE
);
165 while (strlen(slask
) && (slask
[strlen(slask
) - 1] == 13 || slask
[strlen(slask
) - 1] == 10))
167 slask
[strlen(slask
) - 1] = 0;
170 // Read content, save...?
171 if (!current_filename
.size()) // not a file
174 infil
-> fgets(slask
, TMPSIZE
);
175 while (!infil
-> eof() && strncmp(slask
,m_strBoundary
.c_str(),m_strBoundary
.size() ))
178 infil
-> fgets(slask
, TMPSIZE
);
180 // remove trailing cr/linefeed
181 while (val
.size() && (val
[val
.size() - 1] == 13 || val
[val
.size() - 1] == 10))
183 val
= val
.substr(0,val
.size() - 1);
185 cgi
= new CGI(current_name
, val
);
186 m_cgi
.push_back(cgi
);
188 else // current_filename.size() > 0
190 // read until m_strBoundary...
194 char fn
[2000]; // where post'd file will be saved
198 ::GetTempPathA(2000, tmp_path
);
199 if (tmp_path
[strlen(tmp_path
) - 1] != '\\')
201 strcat(tmp_path
, "\\");
203 sprintf(fn
,"%s%s",tmp_path
,current_filename
.c_str());
206 sprintf(fn
,"/tmp/%s",current_filename
.c_str());
208 if ((fil
= fopen(fn
, "wb")) != NULL
)
210 infil
-> fread(&c
,1,1);
211 while (!infil
-> eof())
215 fwrite(&tempcmp
[tc
],1,1,fil
);
226 if (!strncmp(tempcmp
+ tc
+ extra
, m_strBoundary
.c_str(), l
- tc
) &&
227 !strncmp(tempcmp
, m_strBoundary
.c_str() + l
- tc
, tc
))
234 if (!strncmp(tempcmp
+ extra
, m_strBoundary
.c_str(), l
))
239 infil
-> fread(&c
,1,1);
243 cgi
= new CGI(current_name
,fn
,fn
);
244 m_cgi
.push_back(cgi
);
246 strcpy(slask
, m_strBoundary
.c_str());
247 infil
-> fgets(slask
+ strlen(slask
), TMPSIZE
); // next line
251 // couldn't open file
258 // Probably '<m_strBoundary>--'
261 } // while (!infil -> eof())
263 } // if (m_strBoundary)
270 if (strstr(content_type
.c_str(), "x-www-form-urlencoded"))
272 bool got_name
= false; // tnx to FatherNitwit
274 int cl
= (int)content_length
;
276 char *slask
= new char[TMPSIZE
];
277 m_current
= m_cgi
.end();
281 infil
-> fread(&c
,1,1);
283 while (cl
>= 0 && !infil
-> eof())
287 case '=': /* end of name */
293 case '&': /* end of value */
298 cgi
= new CGI(name
,slask
);
303 cgi
= new CGI(slask
, "");
305 m_cgi
.push_back(cgi
);
307 case '+': /* space */
310 case '%': /* hex value */
311 infil
-> fread(&chigh
,1,1);
313 chigh
-= 48 + (chigh
> '9' ? 7 : 0) + (chigh
>= 'a' ? 32 : 0);
314 infil
-> fread(&clow
,1,1);
316 clow
-= 48 + (clow
> '9' ? 7 : 0) + (clow
>= 'a' ? 32 : 0);
317 slask
[i
++] = (char)(chigh
* 16 + clow
);
319 default: /* just another char */
326 infil
-> fread(&c
,1,1);
334 cgi
= new CGI(name
,slask
);
338 cgi
= new CGI(slask
, "");
340 m_cgi
.push_back(cgi
);
346 // HttpdForm(buffer,l) -- request_method GET
348 HttpdForm::HttpdForm(const std::string
& buffer
,size_t l
) : raw(false)
351 char *slask
= new char[TMPSIZE
];
352 char *name
= new char[TMPSIZE
];
356 bool got_name
= false;
358 m_current
= m_cgi
.end();
368 case '=': /* end of name */
374 case '&': /* end of value */
379 cgi
= new CGI(name
,slask
);
384 cgi
= new CGI(slask
, "");
386 m_cgi
.push_back(cgi
);
388 case '+': /* space */
391 case '%': /* hex value */
392 chigh
= buffer
[ptr
++];
393 chigh
-= 48 + (chigh
> '9' ? 7 : 0) + (chigh
>= 'a' ? 32 : 0);
394 clow
= buffer
[ptr
++];
395 clow
-= 48 + (clow
> '9' ? 7 : 0) + (clow
>= 'a' ? 32 : 0);
396 slask
[i
++] = (char)(chigh
* 16 + clow
);
398 default: /* just another char */
407 cgi
= new CGI(name
,slask
);
411 cgi
= new CGI(slask
, "");
413 m_cgi
.push_back(cgi
);
419 HttpdForm::~HttpdForm()
421 CGI
*cgi
= NULL
; //,*tmp;
423 for (cgi_v::iterator it
= m_cgi
.begin(); it
!= m_cgi
.end(); it
++)
431 void HttpdForm::EnableRaw(bool b
)
437 void HttpdForm::strcpyval(std::string
& v
,const char *value
) const
440 for (size_t i
= 0; i
< strlen(value
); i
++)
464 bool HttpdForm::getfirst(std::string
& n
) const
466 m_current
= m_cgi
.begin();
471 bool HttpdForm::getnext(std::string
& n
) const
473 if (m_current
!= m_cgi
.end() )
475 CGI
*current
= *m_current
;
488 bool HttpdForm::getfirst(std::string
& n
,std::string
& v
) const
490 m_current
= m_cgi
.begin();
495 bool HttpdForm::getnext(std::string
& n
,std::string
& v
) const
497 if (m_current
!= m_cgi
.end() )
499 CGI
*current
= *m_current
;
503 v
= current
-> value
;
507 strcpyval(v
,current
-> value
.c_str());
520 int HttpdForm::getvalue(const std::string
& n
,std::string
& v
) const
525 for (cgi_v::const_iterator it
= m_cgi
.begin(); it
!= m_cgi
.end(); it
++)
528 if (cgi
-> name
== n
)
540 strcpyval(v
,cgi
-> value
.c_str());
553 std::string
HttpdForm::getvalue(const std::string
& n
) const
555 for (cgi_v::const_iterator it
= m_cgi
.begin(); it
!= m_cgi
.end(); it
++)
558 if (cgi
-> name
== n
)
567 size_t HttpdForm::getlength(const std::string
& n
) const
572 for (cgi_v::const_iterator it
= m_cgi
.begin(); it
!= m_cgi
.end(); it
++)
575 if (cgi
-> name
== n
)
579 l
= cgi
? cgi
-> value
.size() : 0;
582 for (size_t i
= 0; i
< cgi
-> value
.size(); i
++)
584 switch (cgi
-> value
[i
])
600 HttpdForm::cgi_v
& HttpdForm::getbase()
606 const std::string
& HttpdForm::GetBoundary() const
608 return m_strBoundary
;
612 #ifdef SOCKETS_NAMESPACE