/////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) // // See accompanying file COPYING.TXT file for licensing details. // /////////////////////////////////////////////////////////////////////////////// #define CPPCMS_SOURCE #include #include #include #include #include #include #include #include #include #include "todec.h" namespace cppcms { using impl::cint; struct form_context::_data {}; form_context::form_context() : html_type_(as_html), html_list_type_(as_p), widget_part_type_(first_part), output_(0) { } form_context::form_context(std::ostream &out,form_context::html_type t,form_context::html_list_type hlt) : html_type_(t), html_list_type_(hlt), widget_part_type_(first_part), output_(&out) { } void form_context::html(html_type t) { html_type_=t; } void form_context::html_list(html_list_type t) { html_list_type_ = t; } void form_context::widget_part(widget_part_type t) { widget_part_type_ = t; } void form_context::out(std::ostream &out) { output_ = &out; } std::ostream &form_context::out() const { if(!output_) throw cppcms_error("Can't use form context without assigned output"); return *output_; } form_flags::html_type form_context::html() const { return static_cast(html_type_); } form_flags::html_list_type form_context::html_list() const { return static_cast(html_list_type_); } form_flags::widget_part_type form_context::widget_part() const { return static_cast(widget_part_type_); } form_context::~form_context() { } base_form::base_form() { } base_form::~base_form() { } // Meanwhile -- empty struct form::_data { // TOADD }; form::form() : parent_(0) { } form::~form() { for(unsigned i=0;i(*parent); } form *form::parent() { return parent_; } void form::render(form_context &context) { for(unsigned int i=0;irender(context); } } void form::load(http::context &cont) { for(unsigned int i=0;iload(cont); } } bool form::validate() { bool result=true; for(unsigned int i=0;ivalidate() & result; } return result; } void form::clear() { for(unsigned int i=0;iclear(); } } void form::add(widgets::base_widget &form) { elements_.push_back(widget_type(&form,false)); form.parent(this); } void form::add(form &subform) { elements_.push_back(widget_type(&subform,false)); subform.parent(this); } void form::attach(form *subform) { elements_.push_back(widget_type(subform,true)); subform->parent(this); } void form::attach(widgets::base_widget *subform) { elements_.push_back(widget_type(subform,true)); subform->parent(this); } struct form::iterator::_data {}; form::iterator::iterator() : current_(0), offset_(0) { } form::iterator::iterator(form &f) : current_(&f), offset_(0) { next(); } form::iterator::~iterator() { } form::iterator::iterator(form::iterator const &other) : return_positions_(other.return_positions_), current_(other.current_), offset_(other.offset_), d(other.d) { } form::iterator const &form::iterator::operator=(form::iterator const &other) { if(this != &other) { return_positions_ = other.return_positions_; current_ = other.current_; offset_=other.offset_; d=other.d; } return *this; } bool form::iterator::equal(form::iterator const &other) const { return current_ == other.current_ && offset_ == other.offset_ && return_positions_ == other.return_positions_; } void form::iterator::zero() { current_ = 0; offset_ = 0; } void form::iterator::next() { for(;;) { if(!current_) return; if(offset_ >=current_->elements_.size()) { if(return_positions_.empty()) { zero(); return; } offset_ = return_positions_.top(); return_positions_.pop(); current_ = current_->parent(); } else if(dynamic_cast(current_->elements_[offset_].first)!=0) { offset_++; return; } else { // Elements can be only base_widget or form... so it should be safe current_ = static_cast
(current_->elements_[offset_].first); return_positions_.push(offset_+1); offset_=0; } } } widgets::base_widget *form::iterator::get() const { return static_cast(current_->elements_[offset_ - 1].first); } form::iterator form::begin() { return form::iterator(*this); } form::iterator form::end() { return form::iterator(); } namespace widgets { //////////////////////////////// // widgets::base_widget //////////////////////////////// struct base_widget::_data {}; base_widget::base_widget() : parent_(0), is_valid_(1), is_set_(0), is_disabled_(0), is_readonly_(0), is_generation_done_(0), has_message_(0), has_error_(0), has_help_(0) { } base_widget::~base_widget() { } bool base_widget::has_message() { return has_message_; } bool base_widget::has_help() { return has_help_; } bool base_widget::has_error_message() { return has_error_; } void base_widget::parent(base_form *parent) { parent_=&dynamic_cast(*parent); } form *base_widget::parent() { return parent_; } bool base_widget::set() { return is_set_; } bool base_widget::valid() { return is_valid_; } std::string base_widget::id() { return id_; } std::string base_widget::name() { return name_; } locale::message base_widget::message() { return message_; } locale::message base_widget::error_message() { return error_message_; } locale::message base_widget::help() { return help_; } void base_widget::set(bool v) { is_set_=v; } void base_widget::valid(bool v) { is_valid_=v; } void base_widget::id(std::string v) { id_=v; } void base_widget::name(std::string v) { name_=v; } void base_widget::message(locale::message const &v) { has_message_ = 1; message_=v; } void base_widget::error_message(locale::message const &v) { has_error_ = 1; error_message_=v; } void base_widget::help(locale::message const &v) { has_help_ = 1; help_=v; } void base_widget::message(std::string v) { has_message_ = 1; message_=locale::message("NOTRANS",v); } void base_widget::error_message(std::string v) { has_error_ = 1; error_message_=locale::message("NOTRANS",v); } void base_widget::help(std::string v) { has_help_ = 1; help_=locale::message("NOTRANS",v); } void base_widget::attributes_string(std::string v) { attr_=v; } bool base_widget::disabled() { return is_disabled_; } void base_widget::disabled(bool v) { is_disabled_=v; } bool base_widget::readonly() { return is_readonly_; } void base_widget::readonly(bool v) { is_readonly_=v; } std::string base_widget::attributes_string() { return attr_; } void base_widget::generate(int position,form_context * /*context*/) { if(is_generation_done_) return; if(name_.empty()) { name_ = "_" + impl::todec_string(position); } is_generation_done_ = 1; } void base_widget::pre_load(http::context &context) { context.session().validate_request_origin(); auto_generate(); } void base_widget::auto_generate(form_context *context) { if(is_generation_done_) return; if(parent() == 0) { generate(1,context); return; } form *top; for(top = parent();top->parent();top=top->parent()) ; int i=1; form::iterator p=top->begin(),e=top->end(); while(p!=e) { p->generate(i,context); ++p; ++i; } } void base_widget::render(form_context &context) { auto_generate(&context); std::ostream &output = context.out(); html_list_type type = context.html_list(); switch(type) { case as_p: output<<"

"; break; case as_table: output<<""; break; case as_ul: output<<"

  • "; break; case as_dl: output<<"
    "; break; default: ; } if(has_message()) { if(id_.empty()) output << filters::escape(message()); else output<<""; if(type!=as_table && type!=as_dl) output << " "; } else if(type == as_table) { output<<" "; } switch(context.html_list()) { case as_table: output<<""; break; case as_dl: output<<"
    "; break; default: ; } if(!valid()) { output<<""; if(has_error_message()) output< "; } else if(type == as_table){ output<<" "; } output<<""; context.widget_part(first_part); render_input(context); output<"; if(has_help()) { output<<""<"; } switch(context.html_list()) { case as_p: output<<"

    \n"; break; case as_table: output<<"\n"; break; case as_ul: output<<"
  • \n"; break; case as_dl: output<<"\n"; break; case as_space: output<<"\n"; default: ; } } void base_widget::render_attributes(form_context &context) { auto_generate(&context); std::ostream &output = context.out(); if(!id_.empty()) output << "id=\"" << id_ << "\" "; if(!name_.empty()) output << "name=\"" << name_ << "\" "; if(disabled()) { if(context.html() == as_xhtml) output << "disabled=\"disabled\" "; else output << "disabled "; } } void base_widget::clear() { set(false); } bool base_widget::validate() { valid(true); return true; } //////////////////////////////// // widgets::base_text //////////////////////////////// struct base_text::_data {}; base_text::base_text() : low_(0),high_(-1),validate_charset_(true) { } base_text::~base_text() { } std::string base_text::value() { if(!set()) throw cppcms_error("Value was not loaded"); return value_; } void base_text::value(std::string v) { set(true); value_=v; } void base_text::non_empty() { limits(1,-1); } void base_text::limits(int min,int max) { low_=min; high_=max; } std::pair base_text::limits() { return std::make_pair(low_,high_); } void base_text::validate_charset(bool v) { validate_charset_=v; } bool base_text::validate_charset() { return validate_charset_; } void base_text::load(http::context &context) { pre_load(context); value_.clear(); code_points_ = 0; set(true); // we don't care if the value was set valid(true); if(name().empty()) { return; } http::request::form_type::const_iterator p; p=context.request().post_or_get().find(name()); if(p==context.request().post_or_get().end()) { return; } value_=p->second; if(validate_charset_) { code_points_ = 0; if(!encoding::valid(context.locale(),value_.data(),value_.data()+value_.size(),code_points_)) valid(false); } else { code_points_=value_.size(); } } bool base_text::validate() { if(!valid()) return false; if(!set() && low_==0 && high_==-1) { valid(true); return true; } if(code_points_ < size_t(low_) || (high_ >=0 && code_points_ > size_t(high_))) { valid(false); return false; } return true; } ////////////////////////////////////////////// /// widgets::text ///////////////////////////////////////////// struct text::_data {}; text::~text() {} text::text(): base_html_input("text"), size_(-1) {} text::text(std::string const &type): base_html_input(type), size_(-1) {} void text::size(int n) { size_ = n; } int text::size() { return size_; } void text::render_value(form_context &context) { if(set()) { context.out() << " value=\"" << util::escape(value()) << "\""; } } void text::render_attributes(form_context &context) { base_widget::render_attributes(context); std::ostream &output = context.out(); if(size_ >= 0) output << "size=\"" << cint(size_) <<"\" "; std::pair lm=limits(); if(lm.second >= 0 && validate_charset()) { output << "maxlength=\"" << cint(lm.second) << "\" "; } if(readonly()) { output << "readonly=\"readonly\" "; } } struct base_html_input::_data{}; base_html_input::base_html_input(std::string const &type) : type_(type) {} base_html_input::~base_html_input() {} void base_html_input::render_input(form_context &context) { auto_generate(&context); std::ostream &output=context.out(); if(context.widget_part() == first_part) { output<<""; else output<<" >"; } } ////////////////////////////////////////////// /// widgets::textarea ///////////////////////////////////////////// struct textarea::_data {}; textarea::textarea() : rows_(-1), cols_(-1) {} textarea::~textarea() {} int textarea::rows() { return rows_; } int textarea::cols() { return cols_; } void textarea::rows(int n) { rows_ = n; } void textarea::cols(int n) { cols_ = n; } void textarea::render_input(form_context &context) { std::ostream &output = context.out(); if(context.widget_part() == first_part) { output<<""; } else { output << ">"; } } } //////////////////////// /// Password widget /// //////////////////////// struct password::_data {}; password::password() : text("password"), password_to_check_(0) { } password::~password() { } void password::check_equal(password &p2) { password_to_check_ = &p2; } bool password::validate() { if(!text::validate()) { value(""); return false; } if(password_to_check_) { if(!password_to_check_->set() || !set() || password_to_check_->value()!=value()) { valid(false); value(""); password_to_check_->value(""); return false; } } return true; } struct regex_field::_data {}; regex_field::regex_field() {} regex_field::regex_field(booster::regex const &e) : expression_(e) {} regex_field::regex_field(std::string const &e) : expression_(e) {} regex_field::~regex_field() {} void regex_field::regex(booster::regex const &e) { expression_ = e; } bool regex_field::validate() { if(!text::validate()) return false; valid(set() && booster::regex_match(value(),expression_)); return valid(); } struct email::_data {}; email::email() : regex_field("^[^@]+@[^@]+$") {} email::~email() {} struct checkbox::_data {}; checkbox::checkbox(std::string const &type) : base_html_input(type), identification_("y"), value_(false) { set(true); } checkbox::checkbox() : base_html_input("checkbox"), identification_("y"), value_(false) { set(true); } checkbox::~checkbox() { } bool checkbox::value() { return value_; } void checkbox::value(bool v) { value_=v; } void checkbox::identification(std::string const &id) { identification_=id; } std::string checkbox::identification() { return identification_; } void checkbox::render_value(form_context &context) { if(value()) { if(context.html() == as_xhtml) context.out() << " checked=\"checked\" "; else context.out() << " checked "; } context.out() << "value=\""< range=context.request().post_or_get().equal_range(name()); value(false); while(range.first != range.second) { if(range.first->second == identification_) { value(true); break; } ++range.first; } } struct select_multiple::_data{}; select_multiple::element::element(): selected(0), need_translation(0), original_select(0) { } select_multiple::element::element(std::string const &val,locale::message const &msg,bool sel) : selected(sel), need_translation(1), original_select(sel), id(val), tr_option(msg) { } select_multiple::element::element(std::string const &val,std::string const &msg,bool sel) : selected(sel), need_translation(0), original_select(sel), id(val), str_option(msg) { } select_multiple::select_multiple() : low_(0), high_(std::numeric_limits::max()), rows_(0) { } select_multiple::~select_multiple() { } void select_multiple::add(std::string const &opt,std::string const &id,bool selected) { elements_.push_back(element(id,opt,selected)); } void select_multiple::add(locale::message const &opt,std::string const &id,bool selected) { elements_.push_back(element(id,opt,selected)); } void select_multiple::add(std::string const &opt,bool selected) { std::string id=impl::todec_string(elements_.size()); elements_.push_back(element(id,opt,selected)); } void select_multiple::add(locale::message const &opt,bool selected) { std::string id=impl::todec_string(elements_.size()); elements_.push_back(element(id,opt,selected)); } std::vector select_multiple::selected_map() { std::vector flags(elements_.size(),false); for(unsigned i=0;i select_multiple::selected_ids() { std::set ids; for(unsigned i=0;i range=context.request().post_or_get().equal_range(name()); std::set keys; for(http::request::form_type::const_iterator p=range.first;p!=range.second;++p) keys.insert(p->second); for(unsigned i=0;i 0) out << " size=\"" << cint(rows_) << "\" "; render_attributes(context); } else { out<<" >\n"; for(unsigned i=0;i"; if(el.need_translation) out << filters::escape(el.tr_option); else out << util::escape(el.str_option); out << "\n"; } out << ""; } } void select_multiple::non_empty() { at_least(1); } void select_multiple::at_least(unsigned l) { low_ = l; } void select_multiple::at_most(unsigned h) { high_ = h; } unsigned select_multiple::at_least() { return low_; } unsigned select_multiple::at_most() { return high_; } unsigned select_multiple::rows() { return rows_; } void select_multiple::rows(unsigned v) { rows_=v; } //////////////////// // Select base /////////////////// struct select_base::_data {}; struct select_base::element::_data {}; select_base::element::element() : need_translation(0) { } select_base::element::element(std::string const &v,std::string const &msg) : need_translation(0), id(v), str_option(msg) { } select_base::element::element(std::string const &v,locale::message const &msg) : need_translation(1), id(v), tr_option(msg) { } select_base::element::element(select_base::element const &other) : need_translation(other.need_translation), reserved(other.reserved), id(other.id), str_option(other.str_option), tr_option(other.tr_option) { } select_base::element const &select_base::element::operator=(select_base::element const &other) { if(this!=&other) { need_translation = other.need_translation; reserved = other.reserved; id=other.id; str_option=other.str_option; tr_option=other.tr_option; } return *this; } select_base::element::~element() { } select_base::select_base() : selected_(-1), default_selected_(-1), non_empty_(0) { } select_base::~select_base() { } void select_base::add(std::string const &str,std::string const &id) { elements_.push_back(element(id,str)); } void select_base::add(locale::message const &str,std::string const &id) { elements_.push_back(element(id,str)); } void select_base::add(std::string const &str) { std::string id= impl::todec_string(elements_.size()); elements_.push_back(element(id,str)); } void select_base::add(locale::message const &str) { std::string id= impl::todec_string(elements_.size()); elements_.push_back(element(id,str)); } void select_base::selected(int no) { if(no >= int(elements_.size())) throw cppcms_error("select_base::invalid index"); else if(no < 0) no = -1; selected_ = no; default_selected_ = no; } void select_base::selected_id(std::string id) { if(id.empty()) { selected_ = -1; default_selected_ = -1; return; } for(unsigned i=0;i= int(elements_.size())) return ""; return elements_[selected_].id; } bool select_base::validate() { if(non_empty_ && selected_ == -1) valid(false); else valid(true); return valid(); } void select_base::load(http::context &context) { pre_load(context); set(true); std::pair range=context.request().post_or_get().equal_range(name()); selected_ = -1; http::request::form_type::const_iterator p=range.first; // If empty or more then one if(range.first == range.second || (++range.first)!=range.second ) return; std::string key = p->second; for(unsigned i=0;i\n"; for(unsigned i=0;i"; if(el.need_translation) out << filters::escape(el.tr_option); else out << util::escape(el.str_option); out << "\n"; } out << ""; } } /////////////////////////// /// Radio ///////////////////////// struct radio::_data {}; radio::radio() : vertical_(1) { } radio::~radio() {} void radio::vertical(bool v) { vertical_ = v; } bool radio::vertical() { return vertical_; } void radio::render_input(form_context &context) { auto_generate(&context); std::ostream &out=context.out(); if(context.widget_part() == first_part) { out<<"
    \n"; for(unsigned i=0;i "; else out << "> "; if(el.need_translation) out << filters::escape(el.tr_option); else out << util::escape(el.str_option); if(vertical_) if(context.html() == as_xhtml) out << "
    \n"; else out << "
    \n"; else out << "\n"; } out << "
    "; } } ////////////// // Submit //////////////// struct submit::_data {}; submit::submit() : base_html_input("submit"), pressed_(false) { set(true); } submit::~submit() { } void submit::value(std::string val) { value_=locale::message("NOTRANS",val); } void submit::value(locale::message const &msg) { value_=msg; } void submit::render_value(form_context &context) { context.out() << "value=\"" << filters::escape(value_) << "\" "; } void submit::load(http::context &context) { pre_load(context); set(true); pressed_ = context.request().post_or_get().find(name()) != context.request().post_or_get().end(); } bool submit::value() { return pressed_; } struct hidden::_data{}; hidden::hidden() : text("hidden") {} hidden::~hidden() {} void hidden::render(form_context &context) { auto_generate(&context); std::ostream &output = context.out(); context.widget_part(first_part); render_input(context); output< file::limits() { return std::pair(size_min_,size_max_); } void file::validate_filename_charset(bool v) { check_charset_=v ? 1 : 0; } bool file::validate_filename_charset() { return check_charset_; } booster::shared_ptr file::value() { if(!set()) throw cppcms_error("File was not loaded"); return file_; } void file::mime(std::string const &s) { mime_string_=s; mime_regex_ = booster::regex(); } void file::mime(booster::regex const &r) { mime_string_.clear(); mime_regex_ = r; } void file::add_valid_magic(std::string const &m) { magics_.push_back(m); } void file::load(http::context &context) { pre_load(context); set(false); valid(true); if(name().empty()) return; std::string const field_name = name(); std::vector > files = context.request().files(); for(unsigned i=0;iname()==field_name) { file_=files[i]; set(true); break; } } if(set()) { std::string file_name = file_->filename(); if(check_charset_) { size_t count=0; if(!encoding::valid(context.locale(),file_name.c_str(),file_name.c_str()+file_name.size(),count)) { valid(false); } } } } void file::render_value(form_context &/*context*/) { // Nothing really to do } bool file::validate() { if(!set()) { if(check_non_empty_) { valid(false); return false; } else { valid(true); return true; } } if(!valid()) return false; if(size_max_ != -1 || size_min_!= -1) { size_t file_size = file_->size(); if(size_max_ != -1 && file_size > size_t(size_max_)) { valid(false); return false; } if(size_min_ != -1 && file_size < size_t(size_min_)) { valid(false); return false; } } if(!mime_string_.empty() && file_->mime()!=mime_string_) { valid(false); return false; } if(!mime_regex_.empty() && !booster::regex_match(file_->mime(),mime_regex_)){ valid(false); return false; } if(!filename_regex_.empty() && !booster::regex_match(file_->filename(),filename_regex_)){ valid(false); return false; } if(valid() && !magics_.empty()) { size_t size_max = 0; for(unsigned i=0;i buf(size_max+1,0); file_->data().seekg(0); file_->data().read(&buf.front(),size_max); std::string magic(&buf.front(),file_->data().gcount()); file_->data().seekg(0); valid(false); for(unsigned i=0;i