//----------------------------------------------------------------------------- // parse parameter string // parameter = attribute "=" value // attribute = token // value = token | quoted-string // // If parameter attribute is multiple times given, the values are stored like this: // <attribute>=<value1>,<value2>,..,<value n> //----------------------------------------------------------------------------- bool CWebserverRequest::ParseParams(std::string param_string) { bool ende = false; std::string param, name="", value, number; while(!ende) { if(!ySplitStringExact(param_string,"&",param,param_string)) ende = true; if(ySplitStringExact(param,"=",name,value)) { name = decodeString(name); value = trim(decodeString(value)); if(ParameterList[name].empty()) ParameterList[name] = value; else { ParameterList[name] += ","; ParameterList[name] += value; } } else name = trim(decodeString(name)); number = string_printf("%d", ParameterList.size()+1); log_level_printf(7,"ParseParams: name: %s value: %s\n",name.c_str(), value.c_str()); ParameterList[number] = name; } return true; }
//----------------------------------------------------------------------------- // parse the header of the request // from RFC 2616 / 4.2 Message Header: // message-header = field-name ":" [ field-value ] // field-name = token // field-value = *( field-content | LWS ) // field-content = <the OCTETs making up the field-value // and consisting of either *TEXT or combinations // of token, separators, and quoted-string> //----------------------------------------------------------------------------- bool CWebserverRequest::ParseHeader(std::string header) { bool ende = false; std::string sheader, name, value; HeaderList.clear(); while (!ende) { if (!ySplitStringExact(header, "\r\n", sheader, header)) ende = true; if (ySplitStringExact(sheader, ":", name, value)) HeaderList[name] = trim(value); log_level_printf(8, "ParseHeader: name: %s value: %s\n", name.c_str(), value.c_str()); } return true; }
//----------------------------------------------------------------------------- // HOOK: Hook_ReadConfig // This hook ist called from ReadConfig //----------------------------------------------------------------------------- THandleStatus CmodSendfile::Hook_ReadConfig(CConfigFile *Config, CStringList &ConfigList) { std::string exttypes = Config->getString("mod_sendfile.mime_types", HTTPD_SENDFILE_EXT); ConfigList["mod_sendfile.mime_types"] = exttypes; bool ende = false; std::string item, ext, mime; sendfileTypes.clear(); while(!ende) { if(!ySplitStringExact(exttypes,",",item,exttypes)) ende = true; if(ySplitStringExact(item,":",ext,mime)) { ext = trim(ext); sendfileTypes[ext] = trim(mime); } } return HANDLED_CONTINUE; }
//----------------------------------------------------------------------------- // POST multipart ! FILE UPLOAD! // // No 'Content-type: multipart/mixed' now supported // designed for recursion for different boundaries. // // from RFC 1867: // 2. HTML forms with file submission // // The current HTML specification defines eight possible values for the // attribute TYPE of an INPUT element: CHECKBOX, HIDDEN, IMAGE, // PASSWORD, RADIO, RESET, SUBMIT, TEXT. // // In addition, it defines the default ENCTYPE attribute of the FORM // element using the POST METHOD to have the default value // "application/x-www-form-urlencoded" // // 6. Examples // // Suppose the server supplies the following HTML: // // <FORM ACTION="http://server.dom/cgi/handle" // ENCTYPE="multipart/form-data" // METHOD=POST> // What is your name? <INPUT TYPE=TEXT NAME=submitter> // What files are you sending? <INPUT TYPE=FILE NAME=pics> // </FORM> // // and the user types "Joe Blow" in the name field, and selects a text // file "file1.txt" for the answer to 'What files are you sending?' // // The client might send back the following data: // // Content-type: multipart/form-data, boundary=AaB03x // // --AaB03x // content-disposition: form-data; name="field1" // // Joe Blow // --AaB03x // content-disposition: form-data; name="pics"; filename="file1.txt" // Content-Type: text/plain // // ... contents of file1.txt ... // --AaB03x-- // // 7. Registration of multipart/form-data // // The media-type multipart/form-data follows the rules of all multipart // MIME data streams as outlined in RFC 1521. It is intended for use in // returning the data that comes about from filling out a form. In a // form (in HTML, although other applications may also use forms), there // are a series of fields to be supplied by the user who fills out the // form. Each field has a name. Within a given form, the names are // unique. // // multipart/form-data contains a series of parts. Each part is expected // to contain a content-disposition header where the value is "form- // data" and a name attribute specifies the field name within the form, // e.g., 'content-disposition: form-data; name="xxxxx"', where xxxxx is // the field name corresponding to that field. Field names originally in // non-ASCII character sets may be encoded using the method outlined in // RFC 1522. // // As with all multipart MIME types, each part has an optional Content- // Type which defaults to text/plain. If the contents of a file are // returned via filling out a form, then the file input is identified as // application/octet-stream or the appropriate media type, if known. If // multiple files are to be returned as the result of a single form // entry, they can be returned as multipart/mixed embedded within the // multipart/form-data. // // Each part may be encoded and the "content-transfer-encoding" header // supplied if the value of that part does not conform to the default // encoding. // // File inputs may also identify the file name. The file name may be // described using the 'filename' parameter of the "content-disposition" // header. This is not required, but is strongly recommended in any case // where the original filename is known. This is useful or necessary in // many applications. //----------------------------------------------------------------------------- unsigned int CWebserverRequest::HandlePostBoundary(std::string boundary, unsigned int content_len) { std::string tmp_line; // read boundary tmp_line = Connection->sock->ReceiveLine(); content_len -= tmp_line.length(); log_level_printf(2,"<POST Boundary> Start\n"); if(tmp_line.find(boundary) != std::string::npos) { // is it the boudary end? if(tmp_line.find(boundary+"--") != std::string::npos) { log_level_printf(7,"<POST Boundary> Boundary END found\n"); return 0; } log_level_printf(7,"<POST Boundary> Boundary START found\n"); // read content-disposition: ... tmp_line = Connection->sock->ReceiveLine(); content_len -= tmp_line.length(); if(tmp_line.find("Content-Disposition:") == std::string::npos) { log_level_printf(7,"<POST Boundary> no content-disposition found. line:(%s)\n", tmp_line.c_str()); return 0; } if(tmp_line.find("filename") != std::string::npos) { #ifdef Y_CONFIG_FEATURE_UPLOAD // this part is a file log_level_printf(2,"<POST Boundary> disposition !!this is a file!! found. line:(%s)\n", tmp_line.c_str()); // get para from 'content-disposition: form-data; name="pics"; filename="file1.txt"' // set to ParameterList["<name>"]="<filename>" std::string left, right, var_name, var_value; if(!ySplitStringExact(tmp_line, "name=\"", left, right)) { log_level_printf(7,"<POST Boundary> no var_name START found. line:(%s)\n", tmp_line.c_str()); return 0; } if(!ySplitStringExact(right, "\"", var_name, right)) { log_level_printf(7,"<POST Boundary> no var_name END found. line:(%s)\n", tmp_line.c_str()); return 0; } if(!ySplitStringExact(right, "filename=\"", left, right)) { log_level_printf(7,"<POST Boundary> no filename START found. line:(%s)\n", tmp_line.c_str()); return 0; } if(!ySplitStringExact(right, "\"", var_value, right)) { log_level_printf(7,"<POST Boundary> no filename END found. line:(%s)\n", tmp_line.c_str()); return 0; } var_value = trim(var_value); ParameterList[var_name] = var_value; log_level_printf(7,"<POST Boundary> filename found. name:(%s) value:(%s)\n", var_name.c_str(), var_value.c_str()); //read 'Content-Type: <mime>' tmp_line = Connection->sock->ReceiveLine(); content_len -= tmp_line.length(); // Get Content-Type: put it to ParameterList["<name>_mime"]="<mime>" if(!ySplitStringExact(tmp_line, "Content-Type:", left, right)) { log_level_printf(7,"<POST Boundary> no Content-Type found. line:(%s)\n", tmp_line.c_str()); return 0; } var_value = trim(right); ParameterList[var_name+"_mime"] = var_value; log_level_printf(7,"<POST Boundary> Content-Type found. name:(%s_mime) value:(%s)\n", var_name.c_str(), var_value.c_str()); //read empty line as separator tmp_line = Connection->sock->ReceiveLine(); content_len -= tmp_line.length(); if(tmp_line != "\r\n") { log_level_printf(7,"<POST Boundary> no empty line found. line:(%s)\n", tmp_line.c_str()); return 0; } log_level_printf(7,"<POST Boundary> read file Start\n"); std::string upload_filename; upload_filename = UPLOAD_TMP_FILE; // Hook for Filename naming Connection->HookHandler.Hooks_UploadSetFilename(upload_filename); // Set upload filename to ParameterList["<name>_upload_filename"]="<upload_filename>" ParameterList[var_name+"_upload_filename"] = upload_filename; // open file for write int fd = open(upload_filename.c_str(), O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); if (fd<=0) { aprintf("cannot open file %s: ", upload_filename.c_str()); dperror(""); return 0; } // ASSUMPTION: the complete multipart has no more then SEARCH_BOUNDARY_LEN bytes after the file. // It only works, if no multipart/mixed is used (e.g. in file attachments). Not nessesary in embedded systems. // To speed up uploading, read content_len - SEARCH_BOUNDARY_LEN bytes in blockmode. // To save memory, write them direct into the file. #define SEARCH_BOUNDARY_LEN 2*RECEIVE_BLOCK_LEN // >= RECEIVE_BLOCK_LEN in ySocket unsigned int _readbytes = 0; if((int)content_len - SEARCH_BOUNDARY_LEN >0) { _readbytes = Connection->sock->ReceiveFileGivenLength(fd, content_len - SEARCH_BOUNDARY_LEN); content_len -= _readbytes; log_level_printf(8,"<POST Boundary> read block (already:%d all:%d)\n", _readbytes, content_len); } // read rest of file and check for boundary end _readbytes = 0; bool is_CRLF = false; bool found_end_boundary = false; do { // read line by line tmp_line = Connection->sock->ReceiveLine(); _readbytes += tmp_line.length(); // is this line a boundary? if(tmp_line.find(boundary) != std::string::npos) { if(tmp_line.find(boundary+"--") != std::string::npos) found_end_boundary = true; // it is the end! of POST request! break; // boundary found. end of file. } else // no Boundary: write CRFL if found in last line { if(is_CRLF) if ((unsigned int)write(fd, "\r\n", 2) != 2) { perror("write file failed\n"); return 0; } } // normal line: write it to file // CRLF at end? Maybe CRLF before boundary. Can not decide yet is_CRLF = (tmp_line.length()>=2 && tmp_line[tmp_line.length()-2]=='\r' && tmp_line[tmp_line.length()-1]=='\n'); int write_len = is_CRLF ? tmp_line.length()-2 : tmp_line.length(); if (write(fd, tmp_line.c_str(), write_len) != write_len) { perror("write file failed\n"); return 0; } log_level_printf(2,"<POST Boundary> read file (already:%d all:%d)\n", _readbytes, content_len); } while((_readbytes < content_len) && (tmp_line.length() != 0)); content_len -= _readbytes; close(fd); log_level_printf(2,"<POST Boundary> read file End\n"); if(found_end_boundary) // upload ok? { Connection->HookHandler.Hooks_UploadReady(upload_filename); return 0; } #endif // Y_CONFIG_FEATURE_UPLOAD } else // this part is a POST variable/parameter { // get var_name from 'content-disposition: form-data; name="var_name"' std::string left, right, var_name, var_value; if(!ySplitStringExact(tmp_line, "name=\"", left, right)) { log_level_printf(7,"<POST Boundary> no var_name START found. line:(%s)\n", tmp_line.c_str()); return 0; } if(!ySplitStringExact(right, "\"", var_name, right)) { log_level_printf(7,"<POST Boundary> no var_name END found. line:(%s)\n", tmp_line.c_str()); return 0; } //read empty line as separator tmp_line = Connection->sock->ReceiveLine(); content_len -= tmp_line.length(); if(tmp_line != "\r\n") { log_level_printf(7,"<POST Boundary> no empty line found. line:(%s)\n", tmp_line.c_str()); return 0; } //read var_value line // ASSUMPTION!!!! Only one Line for value, new line is a boundary again // ATTENTION!! var_name must not be unique. So Parameters are store by number too. var_value = Connection->sock->ReceiveLine(); content_len -= tmp_line.length(); var_value = trim(decodeString(var_value)); ParameterList[var_name] = var_value; log_level_printf(7,"<POST Boundary> Parameter found. name:(%s) value:(%s)\n", var_name.c_str(), var_value.c_str()); } } return content_len; }