|
- ///////////////////////////////////////////////////////////////////////////////
- //
- // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
- //
- // See accompanying file COPYING.TXT file for licensing details.
- //
- ///////////////////////////////////////////////////////////////////////////////
- #ifndef CPPCMS_IMPL_MULTIPART_PARSER_H
- #define CPPCMS_IMPL_MULTIPART_PARSER_H
-
- #include <cppcms/defs.h>
- #include <booster/noncopyable.h>
- #include <booster/shared_ptr.h>
- #include <cppcms/http_file.h>
- #include <cppcms/http_content_type.h>
- #include <vector>
-
- #include "http_protocol.h"
-
-
- namespace cppcms {
- namespace impl {
- class multipart_parser : public booster::noncopyable {
- public:
- typedef booster::shared_ptr<http::file> file_ptr;
- typedef std::vector<file_ptr> files_type;
-
- files_type get_files()
- {
- files_type result;
- result.swap(files_);
- return result;
- }
-
- bool set_content_type(std::string const &ct)
- {
- http::content_type t(ct);
- return set_content_type(t);
- }
- bool set_content_type(http::content_type const &content_type)
- {
- std::string bkey = content_type.parameter_by_key("boundary");
- if(bkey.empty())
- return false;
- boundary_ = "\r\n--" + bkey;
- crlfcrlf_ = "\r\n\r\n";
- position_ = 2; // First CRLF does not appear if first state
- state_ = expecting_first_boundary;
- file_.reset(new http::file());
- file_->set_temporary_directory(temp_dir_);
- if(memory_limit_ != -1) {
- file_->set_memory_limit(memory_limit_);
- }
- return true;
- }
-
- multipart_parser(std::string const &temp_dir = std::string(),size_t memory_limit = -1) :
- state_ ( expecting_first_boundary ),
- position_(0),
- temp_dir_(temp_dir),
- memory_limit_(memory_limit),
- file_is_ready_(false)
- {
- }
- ~multipart_parser()
- {
- }
-
- typedef enum {
- parsing_error,
- meta_ready,
- content_partial,
- content_ready,
- continue_input,
- eof,
- no_room_left
- } parsing_result_type;
- static bool is_ok(parsing_result_type r) {
- switch(r) {
- case meta_ready:
- case content_partial:
- case content_ready:
- case continue_input:
- return true;
- default:
- return false;
- }
- }
-
- bool has_file() { return file_is_ready_; }
- http::file &get_file()
- {
- if(!file_is_ready_)
- throw booster::logic_error("Invalid state for get_file");
- return *file_;
- }
- http::file &last_file()
- {
- if(files_.empty())
- throw booster::logic_error("No file was uploaded");
- return *files_.back();
- }
-
- parsing_result_type consume(char const *&buffer,char const *buffer_end)
- {
- for(;buffer!=buffer_end;buffer++) {
- #ifdef DEBUG_MULTIPART_PARSER
- std::cerr << "[";
- if(*buffer < 32)
- std::cerr << int(*buffer);
- else
- std::cerr << *buffer ;
- std::cerr <<"]" << state_str_[state_] << ", pos="<<position_ << std::endl;
- #endif
- switch(state_) {
- case expecting_first_boundary:
- if(*buffer != boundary_[position_])
- return parsing_error;
- position_++;
- if(position_ == boundary_.size()) {
- state_ = expecting_one_crlf_or_eof;
- position_ = 0;
- }
- break;
- case expecting_one_crlf_or_eof:
- if(*buffer=='\r')
- state_=expecting_lf;
- else if(*buffer=='-')
- state_=expecting_minus;
- else
- return parsing_error;
- break;
- case expecting_minus:
- if(*buffer!='-')
- return parsing_error;
- state_=expecting_eof_cr;
- break;
- case expecting_eof_cr:
- if(*buffer!='\r')
- return parsing_error;
- state_=expecting_eof_lf;
- break;
- case expecting_eof_lf:
- if(*buffer!='\n')
- return parsing_error;
- if(buffer + 1 == buffer_end) {
- buffer++;
- return eof;
- }
- else
- return parsing_error;
- case expecting_lf:
- if(*buffer!='\n')
- return parsing_error;
- state_=expecting_crlfcrlf;
- break;
- case expecting_crlfcrlf:
- header_+=*buffer;
- if(*buffer == crlfcrlf_[position_])
- position_++;
- else
- position_=0;
- if(position_ == crlfcrlf_.size()) {
- if(!process_header(header_))
- return parsing_error;
- header_.clear();
- position_ = 0;
- state_ = expecting_separator_boundary;
- buffer++;
- file_is_ready_=true;
- return meta_ready;
- }
- break;
- case expecting_separator_boundary:
- {
- std::streambuf *out=file_->write_data().rdbuf();
- char const *this_boundary = boundary_.c_str();
- size_t boundary_size = boundary_.size();
- while(buffer != buffer_end) {
- char c=*buffer;
- if(c == this_boundary[position_])
- position_++;
- else if(position_ > 0) {
- std::streamsize expected = position_;
- std::streamsize s=out->sputn(this_boundary,position_);
- position_ = 0;
- if(c == boundary_[0])
- position_=1;
- if(s!=expected)
- return no_room_left;
- }
- if(position_ == 0) {
- if(out->sputc(c)==EOF)
- return no_room_left;
- }
- else if(position_ == boundary_size) {
- state_ = expecting_one_crlf_or_eof;
- position_ = 0;
- file_->data().seekg(0);
- files_.push_back(file_);
- file_.reset(new http::file());
- file_->set_temporary_directory(temp_dir_);
- if(memory_limit_ != -1) {
- file_->set_memory_limit(memory_limit_);
- }
- buffer++;
- file_is_ready_=false;
- return content_ready;
- }
- buffer++;
- } // end while
- return content_partial;
- }
- break;
- }
- }
- if(file_is_ready_)
- return content_partial;
- else
- return continue_input;
- }
-
- private:
-
- bool process_header(std::string const &hdr)
- {
- size_t pos = 0;
- while(pos < hdr.size()) {
- size_t next = hdr.find("\r\n",pos);
- if(next==std::string::npos) {
- return false;
- }
- if(next == pos) {
- return true;
- }
- std::string one_header=hdr.substr(pos,next-pos);
- pos=next+2;
- std::string::iterator p=one_header.begin(),e=one_header.end();
- p=http::protocol::skip_ws(p,e);
- std::string::iterator start=p;
- std::string::iterator end=http::protocol::tocken(p,e);
- std::string header_name(start,end);
- p=http::protocol::skip_ws(end,e);
- if(p==e || *p!=':')
- return false;
- p=http::protocol::skip_ws(p+1,e);
-
- if(http::protocol::compare(header_name,"Content-Disposition")==0) {
- start = p;
- end=http::protocol::tocken(p,e);
- if(http::protocol::compare(std::string(start,end),"form-data")!=0)
- return false;
- p=end;
- if(!parse_content_disposition(p,e))
- return false;
- }
- else if(http::protocol::compare(header_name,"Content-Type")==0) {
- http::content_type ct(std::string(p,e));
- file_->mime(ct.media_type());
- }
- }
- return false;
-
- }
-
- bool parse_content_disposition(std::string::iterator p,std::string::iterator e)
- {
- p=http::protocol::skip_ws(p,e);
- while(p!=e) {
- std::string name,value;
- if(!parse_pair(p,e,name,value))
- return false;
- for(unsigned i=0;i<name.size();i++)
- name[i]=http::protocol::ascii_to_lower(name[i]);
- if(name=="filename")
- file_->filename(value);
- else if(name=="name")
- file_->name(value);
- p=http::protocol::skip_ws(p,e);
- }
- return true;
- }
-
- bool parse_pair(std::string::iterator &p,std::string::iterator e,std::string &name,std::string &value)
- {
- if(p==e)
- return false;
- if(*p!=';')
- return false;
- p=http::protocol::skip_ws(p+1,e);
- if(p==e)
- return false;
- std::string::iterator start = p;
- std::string::iterator end = http::protocol::tocken(p,e);
- if(start==end)
- return false;
- if(end==e)
- return false;
- if(*end!='=')
- return false;
- name.assign(start,end);
- p=http::protocol::skip_ws(end+1,e);
- if(p==e)
- return false;
- if(*p=='"') {
- std::string::iterator tmp=p;
- value=http::protocol::unquote(tmp,e);
- if(p==tmp)
- return false;
- p=tmp;
- return true;
- }
- else {
- std::string::iterator tmp=http::protocol::tocken(p,e);
- if(tmp==p)
- return false;
- value.assign(p,tmp);
- p=tmp;
- return true;
- }
- }
-
- typedef enum {
- expecting_first_boundary,
- expecting_one_crlf_or_eof,
- expecting_minus,
- expecting_eof_cr,
- expecting_eof_lf,
- expecting_lf,
- expecting_crlfcrlf,
- expecting_separator_boundary
- } states_type;
-
- #ifdef DEBUG_MULTIPART_PARSER
- static char const * const state_str_[8];
- #endif
-
- states_type state_;
- file_ptr file_;
- files_type files_;
- size_t position_;
- std::string header_;
- std::string boundary_;
- std::string crlfcrlf_;
-
- std::string temp_dir_;
- int memory_limit_;
- bool file_is_ready_;
- };
-
- #ifdef DEBUG_MULTIPART_PARSER
- char const * const multipart_parser::state_str_[8] = {
- "expecting_first_boundary",
- "expecting_one_crlf_or_eof",
- "expecting_minus",
- "expecting_eof_cr",
- "expecting_eof_lf",
- "expecting_lf",
- "expecting_crlfcrlf",
- "expecting_separator_boundary"
- };
- #endif
-
- }
- }
-
-
- #endif
|