ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

369 lines
9.2 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
  4. //
  5. // See accompanying file COPYING.TXT file for licensing details.
  6. //
  7. ///////////////////////////////////////////////////////////////////////////////
  8. #ifndef CPPCMS_IMPL_MULTIPART_PARSER_H
  9. #define CPPCMS_IMPL_MULTIPART_PARSER_H
  10. #include <cppcms/defs.h>
  11. #include <booster/noncopyable.h>
  12. #include <booster/shared_ptr.h>
  13. #include <cppcms/http_file.h>
  14. #include <cppcms/http_content_type.h>
  15. #include <vector>
  16. #include "http_protocol.h"
  17. namespace cppcms {
  18. namespace impl {
  19. class multipart_parser : public booster::noncopyable {
  20. public:
  21. typedef booster::shared_ptr<http::file> file_ptr;
  22. typedef std::vector<file_ptr> files_type;
  23. files_type get_files()
  24. {
  25. files_type result;
  26. result.swap(files_);
  27. return result;
  28. }
  29. bool set_content_type(std::string const &ct)
  30. {
  31. http::content_type t(ct);
  32. return set_content_type(t);
  33. }
  34. bool set_content_type(http::content_type const &content_type)
  35. {
  36. std::string bkey = content_type.parameter_by_key("boundary");
  37. if(bkey.empty())
  38. return false;
  39. boundary_ = "\r\n--" + bkey;
  40. crlfcrlf_ = "\r\n\r\n";
  41. position_ = 2; // First CRLF does not appear if first state
  42. state_ = expecting_first_boundary;
  43. file_.reset(new http::file());
  44. file_->set_temporary_directory(temp_dir_);
  45. if(memory_limit_ != -1) {
  46. file_->set_memory_limit(memory_limit_);
  47. }
  48. return true;
  49. }
  50. multipart_parser(std::string const &temp_dir = std::string(),size_t memory_limit = -1) :
  51. state_ ( expecting_first_boundary ),
  52. position_(0),
  53. temp_dir_(temp_dir),
  54. memory_limit_(memory_limit),
  55. file_is_ready_(false)
  56. {
  57. }
  58. ~multipart_parser()
  59. {
  60. }
  61. typedef enum {
  62. parsing_error,
  63. meta_ready,
  64. content_partial,
  65. content_ready,
  66. continue_input,
  67. eof,
  68. no_room_left
  69. } parsing_result_type;
  70. static bool is_ok(parsing_result_type r) {
  71. switch(r) {
  72. case meta_ready:
  73. case content_partial:
  74. case content_ready:
  75. case continue_input:
  76. return true;
  77. default:
  78. return false;
  79. }
  80. }
  81. bool has_file() { return file_is_ready_; }
  82. http::file &get_file()
  83. {
  84. if(!file_is_ready_)
  85. throw booster::logic_error("Invalid state for get_file");
  86. return *file_;
  87. }
  88. http::file &last_file()
  89. {
  90. if(files_.empty())
  91. throw booster::logic_error("No file was uploaded");
  92. return *files_.back();
  93. }
  94. parsing_result_type consume(char const *&buffer,char const *buffer_end)
  95. {
  96. for(;buffer!=buffer_end;buffer++) {
  97. #ifdef DEBUG_MULTIPART_PARSER
  98. std::cerr << "[";
  99. if(*buffer < 32)
  100. std::cerr << int(*buffer);
  101. else
  102. std::cerr << *buffer ;
  103. std::cerr <<"]" << state_str_[state_] << ", pos="<<position_ << std::endl;
  104. #endif
  105. switch(state_) {
  106. case expecting_first_boundary:
  107. if(*buffer != boundary_[position_])
  108. return parsing_error;
  109. position_++;
  110. if(position_ == boundary_.size()) {
  111. state_ = expecting_one_crlf_or_eof;
  112. position_ = 0;
  113. }
  114. break;
  115. case expecting_one_crlf_or_eof:
  116. if(*buffer=='\r')
  117. state_=expecting_lf;
  118. else if(*buffer=='-')
  119. state_=expecting_minus;
  120. else
  121. return parsing_error;
  122. break;
  123. case expecting_minus:
  124. if(*buffer!='-')
  125. return parsing_error;
  126. state_=expecting_eof_cr;
  127. break;
  128. case expecting_eof_cr:
  129. if(*buffer!='\r')
  130. return parsing_error;
  131. state_=expecting_eof_lf;
  132. break;
  133. case expecting_eof_lf:
  134. if(*buffer!='\n')
  135. return parsing_error;
  136. if(buffer + 1 == buffer_end) {
  137. buffer++;
  138. return eof;
  139. }
  140. else
  141. return parsing_error;
  142. case expecting_lf:
  143. if(*buffer!='\n')
  144. return parsing_error;
  145. state_=expecting_crlfcrlf;
  146. break;
  147. case expecting_crlfcrlf:
  148. header_+=*buffer;
  149. if(*buffer == crlfcrlf_[position_])
  150. position_++;
  151. else
  152. position_=0;
  153. if(position_ == crlfcrlf_.size()) {
  154. if(!process_header(header_))
  155. return parsing_error;
  156. header_.clear();
  157. position_ = 0;
  158. state_ = expecting_separator_boundary;
  159. buffer++;
  160. file_is_ready_=true;
  161. return meta_ready;
  162. }
  163. break;
  164. case expecting_separator_boundary:
  165. {
  166. std::streambuf *out=file_->write_data().rdbuf();
  167. char const *this_boundary = boundary_.c_str();
  168. size_t boundary_size = boundary_.size();
  169. while(buffer != buffer_end) {
  170. char c=*buffer;
  171. if(c == this_boundary[position_])
  172. position_++;
  173. else if(position_ > 0) {
  174. std::streamsize expected = position_;
  175. std::streamsize s=out->sputn(this_boundary,position_);
  176. position_ = 0;
  177. if(c == boundary_[0])
  178. position_=1;
  179. if(s!=expected)
  180. return no_room_left;
  181. }
  182. if(position_ == 0) {
  183. if(out->sputc(c)==EOF)
  184. return no_room_left;
  185. }
  186. else if(position_ == boundary_size) {
  187. state_ = expecting_one_crlf_or_eof;
  188. position_ = 0;
  189. file_->data().seekg(0);
  190. files_.push_back(file_);
  191. file_.reset(new http::file());
  192. file_->set_temporary_directory(temp_dir_);
  193. if(memory_limit_ != -1) {
  194. file_->set_memory_limit(memory_limit_);
  195. }
  196. buffer++;
  197. file_is_ready_=false;
  198. return content_ready;
  199. }
  200. buffer++;
  201. } // end while
  202. return content_partial;
  203. }
  204. break;
  205. }
  206. }
  207. if(file_is_ready_)
  208. return content_partial;
  209. else
  210. return continue_input;
  211. }
  212. private:
  213. bool process_header(std::string const &hdr)
  214. {
  215. size_t pos = 0;
  216. while(pos < hdr.size()) {
  217. size_t next = hdr.find("\r\n",pos);
  218. if(next==std::string::npos) {
  219. return false;
  220. }
  221. if(next == pos) {
  222. return true;
  223. }
  224. std::string one_header=hdr.substr(pos,next-pos);
  225. pos=next+2;
  226. std::string::iterator p=one_header.begin(),e=one_header.end();
  227. p=http::protocol::skip_ws(p,e);
  228. std::string::iterator start=p;
  229. std::string::iterator end=http::protocol::tocken(p,e);
  230. std::string header_name(start,end);
  231. p=http::protocol::skip_ws(end,e);
  232. if(p==e || *p!=':')
  233. return false;
  234. p=http::protocol::skip_ws(p+1,e);
  235. if(http::protocol::compare(header_name,"Content-Disposition")==0) {
  236. start = p;
  237. end=http::protocol::tocken(p,e);
  238. if(http::protocol::compare(std::string(start,end),"form-data")!=0)
  239. return false;
  240. p=end;
  241. if(!parse_content_disposition(p,e))
  242. return false;
  243. }
  244. else if(http::protocol::compare(header_name,"Content-Type")==0) {
  245. http::content_type ct(std::string(p,e));
  246. file_->mime(ct.media_type());
  247. }
  248. }
  249. return false;
  250. }
  251. bool parse_content_disposition(std::string::iterator p,std::string::iterator e)
  252. {
  253. p=http::protocol::skip_ws(p,e);
  254. while(p!=e) {
  255. std::string name,value;
  256. if(!parse_pair(p,e,name,value))
  257. return false;
  258. for(unsigned i=0;i<name.size();i++)
  259. name[i]=http::protocol::ascii_to_lower(name[i]);
  260. if(name=="filename")
  261. file_->filename(value);
  262. else if(name=="name")
  263. file_->name(value);
  264. p=http::protocol::skip_ws(p,e);
  265. }
  266. return true;
  267. }
  268. bool parse_pair(std::string::iterator &p,std::string::iterator e,std::string &name,std::string &value)
  269. {
  270. if(p==e)
  271. return false;
  272. if(*p!=';')
  273. return false;
  274. p=http::protocol::skip_ws(p+1,e);
  275. if(p==e)
  276. return false;
  277. std::string::iterator start = p;
  278. std::string::iterator end = http::protocol::tocken(p,e);
  279. if(start==end)
  280. return false;
  281. if(end==e)
  282. return false;
  283. if(*end!='=')
  284. return false;
  285. name.assign(start,end);
  286. p=http::protocol::skip_ws(end+1,e);
  287. if(p==e)
  288. return false;
  289. if(*p=='"') {
  290. std::string::iterator tmp=p;
  291. value=http::protocol::unquote(tmp,e);
  292. if(p==tmp)
  293. return false;
  294. p=tmp;
  295. return true;
  296. }
  297. else {
  298. std::string::iterator tmp=http::protocol::tocken(p,e);
  299. if(tmp==p)
  300. return false;
  301. value.assign(p,tmp);
  302. p=tmp;
  303. return true;
  304. }
  305. }
  306. typedef enum {
  307. expecting_first_boundary,
  308. expecting_one_crlf_or_eof,
  309. expecting_minus,
  310. expecting_eof_cr,
  311. expecting_eof_lf,
  312. expecting_lf,
  313. expecting_crlfcrlf,
  314. expecting_separator_boundary
  315. } states_type;
  316. #ifdef DEBUG_MULTIPART_PARSER
  317. static char const * const state_str_[8];
  318. #endif
  319. states_type state_;
  320. file_ptr file_;
  321. files_type files_;
  322. size_t position_;
  323. std::string header_;
  324. std::string boundary_;
  325. std::string crlfcrlf_;
  326. std::string temp_dir_;
  327. int memory_limit_;
  328. bool file_is_ready_;
  329. };
  330. #ifdef DEBUG_MULTIPART_PARSER
  331. char const * const multipart_parser::state_str_[8] = {
  332. "expecting_first_boundary",
  333. "expecting_one_crlf_or_eof",
  334. "expecting_minus",
  335. "expecting_eof_cr",
  336. "expecting_eof_lf",
  337. "expecting_lf",
  338. "expecting_crlfcrlf",
  339. "expecting_separator_boundary"
  340. };
  341. #endif
  342. }
  343. }
  344. #endif