Fix CGI form/cookie data handling and CGI URL encode/decode

- CGIFormData: fix broken setSize()+read() double-length bug that
  corrupted every parsed body with a leading run of garbage bytes
  (setSize(contentLength) pre-set length before read()/write()
  appended on top of it); switch content-type checks from exact
  equality to startsWith() since a real multipart Content-Type
  always carries a boundary= parameter; implement the previously
  empty multipart/form-data branch (ordinary fields only, file
  uploads deferred to a separate upload mechanism per TODO.txt);
  restore operator[] real lookup logic (was hardcoded to echo the
  key back); default-initialize formdata to NULL; fix mismatched
  include guard (leftover from an IMFFormData.h copy-paste); drop
  the unused IMFHeader.h include that pulled in a broken,
  unrelated StreamReader.h dependency.
- CGIFormattedData: parameterize the split delimiter (was hardcoded
  to &, which silently broke cookie parsing since HTTP_COOKIE uses
  ; not &) and trim whitespace on each split token.
- MString::toCGI()/fromCGI(): were unimplemented stubs (always
  returned an empty string); implemented proper CGI percent-encode
  / percent-decode + space plus-encoding, and fixed the one caller
  that discarded fromCGIs return value instead of using it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Brad ArantandClaude Sonnet 5 committed 2026-08-22 21:37:10 -07:00
1 parent 28feaff3d7
commit 94d4a30e88
5 files changed
+111 -24

No files matched your search

+40 -10
View File
@@ -1,28 +1,58 @@
#include "CGIFormData.h" #include "CGIFormData.h"
#include "IMFHeader.h"
#include "malloc.h" #include "malloc.h"
namespace coreutils { namespace coreutils {
CGIFormData::CGIFormData(int fd, ZString contentType, int contentLength, ZString boundary) CGIFormData::CGIFormData(int fd, ZString contentType, int contentLength, ZString boundary)
: contentType(contentType) { : contentType(contentType) {
if(contentType == "application/x-www-form-urlencoded") { if(contentType.startsWith("application/x-www-form-urlencoded")) {
setSize(contentLength);
read(fd, contentLength); read(fd, contentLength);
formdata = new CGIFormattedData(*this); formdata = new CGIFormattedData(*this);
} else if(contentType == "multipart/form-data") { } else if(contentType.startsWith("multipart/form-data")) {
read(fd, contentLength);
formdata = new CGIFormattedData();
MString delimiter;
delimiter << "--" << boundary;
ZString body(*this);
body.split(delimiter);
for(int ix = 0; ix < body.getList().size(); ++ix) {
ZString part = body[ix];
if(part.getLength() == 0 || part.startsWith("--"))
continue;
int headerEnd = part.find("\r\n\r\n");
if(headerEnd == -1)
continue;
ZString headers = part.substring(0, headerEnd);
if(headers.find("filename=\"") != -1)
// file uploads are out of scope here; handled via a separate upload mechanism.
continue;
int namePos = headers.find("name=\"");
if(namePos == -1)
continue;
ZString afterName = headers.substring(namePos + 6);
int nameEnd = afterName.find("\"");
if(nameEnd == -1)
continue;
ZString name = afterName.substring(0, nameEnd);
ZString value = part.substring(headerEnd + 4);
value.trimCRLF();
formdata->data[name] = value;
}
} }
} }
MString CGIFormData::operator[](ZString name) { MString CGIFormData::operator[](ZString name) {
// if(contentType == "application/x-www-form-urlencoded") if(formdata == NULL)
// return formdata[name]; return MString();
MString temp(name); return (*formdata).data[name];
return temp;
} }
} }
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef __CGIFormData_h__ #ifndef __CGIFormData_h__
# define __IMFFormData_h__ # define __CGIFormData_h__
#include "MString.h" #include "MString.h"
#include "ZString.h" #include "ZString.h"
@@ -15,7 +15,7 @@ namespace coreutils {
MString operator[](ZString name); MString operator[](ZString name);
private: private:
CGIFormattedData *formdata; CGIFormattedData *formdata = NULL;
MString contentType; MString contentType;
}; };
+18 -12
View File
@@ -6,25 +6,31 @@ namespace coreutils {
CGIFormattedData::CGIFormattedData() {} CGIFormattedData::CGIFormattedData() {}
CGIFormattedData::CGIFormattedData(ZString cgiData) { CGIFormattedData::CGIFormattedData(ZString cgiData) {
parse(cgiData);
}
CGIFormattedData::CGIFormattedData(ZString cgiData, ZString delimiter) : delimiter(delimiter) {
parse(cgiData);
}
void CGIFormattedData::setDelimiter(ZString delimiter) {
this->delimiter = delimiter;
}
void CGIFormattedData::parse(ZString cgiData) {
if(cgiData != "") { if(cgiData != "") {
cgiData.split("&"); cgiData.split(delimiter);
for(int ix = 0; ix < cgiData.getList().size(); ++ix) { for(int ix = 0; ix < cgiData.getList().size(); ++ix) {
cgiData[ix].split("="); ZString pair = cgiData[ix].trim();
data[cgiData[ix][0]] = cgiData[ix][1]; pair.split("=");
data[cgiData[ix][0]].fromCGI(); data[pair[0]] = pair[1];
data[pair[0]] = data[pair[0]].fromCGI();
} }
} }
} }
ZString CGIFormattedData::operator=(ZString cgiData) { ZString CGIFormattedData::operator=(ZString cgiData) {
if(cgiData != "") { parse(cgiData);
cgiData.split("&");
for(int ix = 0; ix < cgiData.getList().size(); ++ix) {
cgiData[ix].split("=");
data[cgiData[ix][0]] = cgiData[ix][1];
data[cgiData[ix][0]].fromCGI();
}
}
return cgiData; return cgiData;
} }
} }
+7
View File
@@ -17,11 +17,18 @@ namespace coreutils {
public: public:
CGIFormattedData(); CGIFormattedData();
CGIFormattedData(ZString cgiData); CGIFormattedData(ZString cgiData);
CGIFormattedData(ZString cgiData, ZString delimiter);
ZString operator=(ZString cgiData); ZString operator=(ZString cgiData);
void setDelimiter(ZString delimiter);
std::map<MString, MString> data; std::map<MString, MString> data;
private:
void parse(ZString cgiData);
ZString delimiter = "&";
}; };
} }
+44
View File
@@ -432,13 +432,57 @@ namespace coreutils {
MString MString::toCGI() { MString MString::toCGI() {
push();
reset();
MString target; MString target;
while(!eod()) {
char ch = nextChar();
if(ch == ' ') {
target.write('+');
} else if(isalnum((unsigned char)ch) || ch == '-' || ch == '_' || ch == '.' || ch == '~') {
target.write(ch);
} else {
target.write('%');
target.write(hexChar((ch >> 4) & 15));
target.write(hexChar(ch & 15));
}
}
pop();
return target; return target;
} }
MString MString::fromCGI() { MString MString::fromCGI() {
push();
reset();
MString target; MString target;
while(!eod()) {
char ch = nextChar();
if(ch == '+') {
target.write(' ');
} else if(ch == '%' && !eod()) {
char ch1 = nextChar();
ch1 -= 48;
if(ch1 > 9)
ch1 -= 7;
ch1 <<= 4;
ch1 &= 240;
if(!eod()) {
char ch2 = nextChar();
ch2 -= 48;
if(ch2 > 9)
ch2 -= 7;
ch2 &= 15;
ch1 |= ch2;
target.write(ch1);
} else {
target.write(ch1);
}
} else {
target.write(ch);
}
}
pop();
return target; return target;
} }