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.
 
 
 
 
 
 

404 lines
8.2 KiB

  1. #define CPPCMS_SOURCE
  2. #include "session_interface.h"
  3. #include "session_pool.h"
  4. #include "session_api.h"
  5. #include "util.h"
  6. #include "http_context.h"
  7. #include "http_request.h"
  8. #include "http_response.h"
  9. #include "http_cookie.h"
  10. #include "cppcms_error.h"
  11. #include "service.h"
  12. #include "json.h"
  13. #include <sstream>
  14. #include "string.h"
  15. using namespace std;
  16. namespace cppcms {
  17. struct session_interface::data {};
  18. struct session_interface::entry {
  19. std::string value;
  20. bool exposed;
  21. entry(std::string v="",bool exp=false) : value(v) , exposed(exp) {}
  22. bool operator==(entry const &other) const
  23. {
  24. return value==other.value && exposed==other.exposed;
  25. }
  26. bool operator!=(entry const &other) const
  27. {
  28. return !(*this==other);
  29. }
  30. };
  31. session_interface::session_interface(http::context &context) :
  32. context_(&context),
  33. loaded_(0)
  34. {
  35. timeout_val_def_=context_->settings().get("session.timeout",24*3600);
  36. string s_how=context_->settings().get("session.expire","browser");
  37. if(s_how=="fixed") {
  38. how_def_=fixed;
  39. }
  40. else if(s_how=="renew") {
  41. how_def_=renew;
  42. }
  43. else if(s_how=="browser") {
  44. how_def_=browser;
  45. }
  46. else {
  47. throw cppcms_error("Unsupported `session.expire' type `"+s_how+"'");
  48. }
  49. storage_=context_->service().session_pool().get();
  50. }
  51. session_interface::~session_interface()
  52. {
  53. }
  54. bool session_interface::load()
  55. {
  56. if(loaded_)
  57. return false; // FIXME
  58. loaded_ = 1;
  59. if(!storage_.get())
  60. return false;
  61. data_.clear();
  62. data_copy_.clear();
  63. timeout_val_=timeout_val_def_;
  64. how_=how_def_;
  65. string ar;
  66. saved_=0;
  67. on_server_=0;
  68. if(!storage_->load(*this,ar,timeout_in_)) {
  69. return false;
  70. }
  71. load_data(data_,ar);
  72. data_copy_=data_;
  73. if(is_set("_t"))
  74. timeout_val_=get<int>("_t");
  75. if(is_set("_h"))
  76. how_=get<int>("_h");
  77. if(is_set("_s"))
  78. on_server_=get<int>("_s");
  79. return true;
  80. }
  81. int session_interface::cookie_age()
  82. {
  83. if(how_==browser)
  84. return 0;
  85. if(how_==renew || ( how_==fixed && new_session_ ))
  86. return timeout_val_;
  87. return timeout_in_ - time(NULL);
  88. }
  89. time_t session_interface::session_age()
  90. {
  91. if(how_==browser || how_==renew || (how_==fixed && new_session_))
  92. return timeout_val_ + time(NULL);
  93. return timeout_in_;
  94. }
  95. namespace {
  96. struct packed {
  97. uint32_t key_size : 10;
  98. uint32_t exposed : 1;
  99. uint32_t data_size : 21;
  100. packed() {}
  101. packed(unsigned ks,bool exp, unsigned ds)
  102. {
  103. if(ks >=1024)
  104. throw cppcms_error("session::save key too long");
  105. if(ds >= 1024 * 1024 * 2)
  106. throw cppcms_error("session::save value too long");
  107. key_size=ks;
  108. exposed = exp ? 1 : 0;
  109. data_size=ds;
  110. }
  111. packed(char const *start,char const *end)
  112. {
  113. if(start + 4 <= end ) {
  114. memcpy(this,start,4);
  115. }
  116. else
  117. throw cppcms_error("session::format violation -> pack");
  118. }
  119. };
  120. }
  121. void session_interface::save_data(data_type const &data,std::string &s)
  122. {
  123. s.clear();
  124. data_type::const_iterator p;
  125. for(p=data.begin();p!=data.end();++p) {
  126. packed header(p->first.size(),p->second.exposed,p->second.value.size());
  127. char *ptr=(char *)&header;
  128. s.append(ptr,ptr+sizeof(header));
  129. s.append(p->first.begin(),p->first.end());
  130. s.append(p->second.value.begin(),p->second.value.end());
  131. }
  132. }
  133. void session_interface::load_data(data_type &data,std::string const &s)
  134. {
  135. data.clear();
  136. char const *begin=s.data(),*end=begin+s.size();
  137. while(begin < end) {
  138. packed p(begin,end);
  139. begin +=sizeof(p);
  140. if(end - begin >= int(p.key_size + p.data_size)) {
  141. string key(begin,begin+p.key_size);
  142. begin+=p.key_size;
  143. string val(begin,begin+p.data_size);
  144. begin+=p.data_size;
  145. entry &ent=data[key];
  146. ent.exposed = p.exposed;
  147. ent.value.swap(val);
  148. }
  149. else {
  150. throw cppcms_error("sessions::format violation data");
  151. }
  152. }
  153. }
  154. void session_interface::update_exposed(bool force)
  155. {
  156. for(data_type::iterator p=data_.begin();p!=data_.end();++p) {
  157. data_type::iterator p2=data_copy_.find(p->first);
  158. if(p->second.exposed && (force || p2==data_copy_.end() || !p2->second.exposed || p->second.value!=p2->second.value)){
  159. set_session_cookie(cookie_age(),p->second.value,p->first);
  160. }
  161. else if(!p->second.exposed && ((p2!=data_copy_.end() && p2->second.exposed) || force)) {
  162. set_session_cookie(-1,"",p->first);
  163. }
  164. }
  165. for(data_type::iterator p=data_copy_.begin();p!=data_copy_.end();++p) {
  166. if(p->second.exposed && data_.find(p->first)==data_.end()) {
  167. set_session_cookie(-1,"",p->first);
  168. }
  169. }
  170. }
  171. void session_interface::save()
  172. {
  173. if(storage_.get()==NULL || !loaded_ || saved_)
  174. return;
  175. check();
  176. new_session_ = data_copy_.empty() && !data_.empty();
  177. if(data_.empty()) {
  178. if(get_session_cookie()!="")
  179. storage_->clear(*this);
  180. update_exposed(true);
  181. return;
  182. }
  183. time_t now = time(NULL);
  184. bool force_update=false;
  185. if(data_==data_copy_) {
  186. if(how_==fixed) {
  187. return;
  188. }
  189. if(how_==renew || how_==browser) {
  190. int64_t delta=now + timeout_val_ - timeout_in_;
  191. if(delta < timeout_val_ * 0.1) {// Less then 10% -- no renew need
  192. return;
  193. }
  194. }
  195. force_update=true;
  196. }
  197. string ar;
  198. save_data(data_,ar);
  199. temp_cookie_.clear();
  200. storage_->save(*this,ar,session_age(),new_session_,on_server_);
  201. set_session_cookie(cookie_age(),temp_cookie_);
  202. temp_cookie_.clear();
  203. update_exposed(force_update);
  204. saved_=true;
  205. }
  206. void session_interface::check()
  207. {
  208. if(storage_.get()==NULL)
  209. throw cppcms_error("Session storage backend is not loaded\n");
  210. }
  211. string &session_interface::operator[](string const &key)
  212. {
  213. check();
  214. return data_[key].value;
  215. }
  216. void session_interface::erase(string const &key)
  217. {
  218. check();
  219. data_.erase(key);
  220. }
  221. bool session_interface::is_set(string const &key)
  222. {
  223. check();
  224. return data_.find(key)!=data_.end();
  225. }
  226. void session_interface::clear()
  227. {
  228. check();
  229. data_.clear();
  230. }
  231. std::string session_interface::get(std::string const &key)
  232. {
  233. check();
  234. data_type::const_iterator p=data_.find(key);
  235. if(p==data_.end())
  236. throw cppcms_error("Undefined session key "+key);
  237. return p->second.value;
  238. }
  239. void session_interface::set(std::string const &key,std::string const &v)
  240. {
  241. check();
  242. data_[key]=v;
  243. }
  244. void session_interface::clear_session_cookie()
  245. {
  246. check();
  247. if(get_session_cookie()!="")
  248. set_session_cookie(-1,"");
  249. }
  250. void session_interface::set_session_cookie(int64_t age,string const &data,string const &key)
  251. {
  252. if(data.empty())
  253. age=-1;
  254. std::string cookie_name=context_->settings().get("session.cookies.prefix","cppcms_session");
  255. if(!key.empty()) {
  256. cookie_name+="_";
  257. cookie_name+=key;
  258. }
  259. std::string domain = context_->settings().get("session.cookies.domain","");
  260. std::string path = context_->settings().get("session.cookies.path","/");
  261. bool secure = context_->settings().get("session.cookies.secure",0);
  262. http::cookie the_cookie(cookie_name,util::urlencode(data),path,domain);
  263. if(age < 0) {
  264. the_cookie.max_age(0);
  265. }
  266. else if(age == 0)
  267. the_cookie.browser_age();
  268. else
  269. the_cookie.max_age(age);
  270. the_cookie.secure(secure);
  271. context_->response().set_cookie(the_cookie);
  272. }
  273. void session_interface::set_session_cookie(string const &data)
  274. {
  275. check();
  276. temp_cookie_=data;
  277. }
  278. string session_interface::get_session_cookie()
  279. {
  280. check();
  281. string name=context_->settings().get("session.cookies.prefix","cppcms_session");
  282. http::request::cookies_type const &cookies = context_->request().cookies();
  283. http::request::cookies_type::const_iterator p=cookies.find(name);
  284. if(p==cookies.end())
  285. return "";
  286. return p->second.value();
  287. }
  288. bool session_interface::is_exposed(std::string const &key)
  289. {
  290. data_type::iterator p=data_.find(key);
  291. if(p!=data_.end())
  292. return p->second.exposed;
  293. return false;
  294. }
  295. void session_interface::expose(string const &key,bool exp)
  296. {
  297. data_[key].exposed=exp;
  298. }
  299. void session_interface::hide(string const &key)
  300. {
  301. check();
  302. expose(key,false);
  303. }
  304. void session_interface::age(int t)
  305. {
  306. check();
  307. timeout_val_=t;
  308. set("_t",t);
  309. }
  310. int session_interface::age()
  311. {
  312. check();
  313. return timeout_val_;
  314. }
  315. void session_interface::default_age()
  316. {
  317. check();
  318. erase("_t");
  319. timeout_val_=timeout_val_def_;
  320. }
  321. int session_interface::expiration()
  322. {
  323. check();
  324. return how_;
  325. }
  326. void session_interface::expiration(int h)
  327. {
  328. check();
  329. how_=h;
  330. set("_h",h);
  331. }
  332. void session_interface::default_expiration()
  333. {
  334. check();
  335. erase("_h");
  336. how_=how_def_;
  337. }
  338. void session_interface::on_server(bool srv)
  339. {
  340. check();
  341. on_server_=srv;
  342. set("_s",int(srv));
  343. }
  344. bool session_interface::on_server()
  345. {
  346. check();
  347. return on_server_;
  348. }
  349. } // cppcms