#ifndef __CGIFormData_h__ # define __CGIFormData_h__ #include "Parser.h" #include "IMFHeader.h" #include "IMFBoundary.h" #include namespace coreutils { /// /// CGIFormData parses a CGI request body (application/x-www-form-urlencoded /// or multipart/form-data) from the shared StreamReader and exposes the /// resulting name/value pairs via operator[] - the object a caller (e.g. /// JetCore's Tag::getVariable, for its $[:name] CGI variable syntax) holds /// onto and indexes by variable name once doParse() reports COMPLETE. /// /// Resumable like every other Parser here: for multipart/form-data, each /// part's boundary line and header block are parsed via IMFBoundary/ /// IMFHeader (chained through checkParse(), sharing this same /// StreamReader), so a part - or even its boundary marker - can straddle /// any number of feed() calls without losing state. File-upload parts /// (Content-Disposition with a filename= parameter) are recognized and /// skipped - matching this codebase's existing convention that uploads /// get a separate mechanism, not buffered through here. /// class CGIFormData : public Parser { public: CGIFormData(StreamReader &reader, ZString contentType, int contentLength, ZString boundary); virtual ~CGIFormData(); MString operator[](ZString name); protected: Status parse() override; private: enum Mode { URLENCODED, MULTIPART, UNKNOWN } mode; enum State { INIT, OPENING_TAIL, PART_HEADERS, PART_BODY, PART_BOUNDARY, URLENCODED_BODY, DONE } state = INIT; ZString contentType; int contentLength; ZString boundary; std::map data; IMFHeader *headerParser = NULL; IMFBoundary *boundaryParser = NULL; MString currentName; bool currentSkip = false; std::map nameCounts; void extractPartName(IMFHeader &header); /// /// Stores value under "name:N", where N is how many values have /// already been stored under this same base name (0 for the first, /// 1 for the second, etc.) - matches the indexed key format /// JetCore's Tag::getVariable's $[:name] syntax looks up by /// (defaulting to :0 when the caller doesn't specify an index), /// supporting repeated field names (checkboxes, multi-select) the /// same way. /// void storeValue(MString name, MString value); }; } #endif