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.
 
 
 
 
 
 

546 lines
14 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. #define CPPCMS_SOURCE
  9. // make sure we all defines are given
  10. #include "dir.h"
  11. #include <cppcms/config.h>
  12. # if defined(CPPCMS_HAVE_CANONICALIZE_FILE_NAME) && !defined(_GNU_SOURCE)
  13. # define _GNU_SOURCE
  14. #endif
  15. #include <stdlib.h>
  16. #include <booster/callback.h>
  17. #include <cppcms/application.h>
  18. #include <cppcms/service.h>
  19. #include <cppcms/http_response.h>
  20. #include <cppcms/http_context.h>
  21. #include "internal_file_server.h"
  22. #include <cppcms/cppcms_error.h>
  23. #include <cppcms/json.h>
  24. #include <cppcms/util.h>
  25. #include <sstream>
  26. #include <booster/nowide/fstream.h>
  27. #include <booster/locale/encoding.h>
  28. #include <booster/locale/formatting.h>
  29. #include <string.h>
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32. #ifndef CPPCMS_WIN_NATIVE
  33. #include <unistd.h>
  34. #include <limits.h>
  35. #endif
  36. namespace cppcms {
  37. namespace impl {
  38. file_server::file_server(cppcms::service &srv,bool async) : application(srv), async_(async)
  39. {
  40. if(!canonical(settings().get("file_server.document_root","."),document_root_))
  41. throw cppcms_error("Invalid document root");
  42. list_directories_ = settings().get("file_server.listing",false);
  43. index_file_ = settings().get("file_server.index","index.html");
  44. check_symlinks_ = settings().get("file_server.check_symlink",true);
  45. std::string mime_file=settings().get("file_server.mime_types","");
  46. allow_deflate_ = settings().get("file_server.allow_deflate",false);
  47. if(settings().find("file_server.alias").type()==json::is_array) {
  48. json::array const &alias = settings().find("file_server.alias").array();
  49. for(unsigned i=0;i<alias.size();i++) {
  50. std::string url = alias[i].get<std::string>("url");
  51. if(url.size() < 2 || url[0]!='/') {
  52. throw cppcms_error("Invalid alias URL: " + url);
  53. }
  54. if(url[url.size()-1]=='/')
  55. url.resize(url.size()-1);
  56. std::string input_path = alias[i].get<std::string>("path");
  57. std::string canon_path;
  58. if(!canonical(input_path,canon_path)) {
  59. throw cppcms_error("Invalid alias path: " + input_path);
  60. }
  61. alias_.push_back(std::make_pair(url,canon_path));
  62. }
  63. }
  64. if(mime_file.empty()) {
  65. mime_[".pdf"] = "application/pdf";
  66. mime_[".sig"] = "application/pgp-signature";
  67. mime_[".spl"] = "application/futuresplash";
  68. mime_[".ps"] = "application/postscript";
  69. mime_[".torrent"]= "application/x-bittorrent";
  70. mime_[".dvi"] = "application/x-dvi";
  71. mime_[".gz"] = "application/x-gzip";
  72. mime_[".pac"] = "application/x-ns-proxy-autoconfig";
  73. mime_[".swf"] = "application/x-shockwave-flash";
  74. mime_[".tgz"] = "application/x-tgz";
  75. mime_[".tar"] = "application/x-tar";
  76. mime_[".zip"] = "application/zip";
  77. mime_[".mp3"] = "audio/mpeg";
  78. mime_[".m3u"] = "audio/x-mpegurl";
  79. mime_[".wma"] = "audio/x-ms-wma";
  80. mime_[".wax"] = "audio/x-ms-wax";
  81. mime_[".ogg"] = "application/ogg";
  82. mime_[".wav"] = "audio/x-wav";
  83. mime_[".gif"] = "image/gif";
  84. mime_[".jpg"] = "image/jpeg";
  85. mime_[".jpeg"] = "image/jpeg";
  86. mime_[".png"] = "image/png";
  87. mime_[".xbm"] = "image/x-xbitmap";
  88. mime_[".xpm"] = "image/x-xpixmap";
  89. mime_[".xwd"] = "image/x-xwindowdump";
  90. mime_[".css"] = "text/css";
  91. mime_[".html"] = "text/html";
  92. mime_[".htm"] = "text/html";
  93. mime_[".js"] = "text/javascript";
  94. mime_[".asc"] = "text/plain";
  95. mime_[".c"] = "text/plain";
  96. mime_[".cpp"] = "text/plain";
  97. mime_[".log"] = "text/plain";
  98. mime_[".conf"] = "text/plain";
  99. mime_[".text"] = "text/plain";
  100. mime_[".txt"] = "text/plain";
  101. mime_[".dtd"] = "text/xml";
  102. mime_[".xml"] = "text/xml";
  103. mime_[".mpeg"] = "video/mpeg";
  104. mime_[".mpg"] = "video/mpeg";
  105. mime_[".mov"] = "video/quicktime";
  106. mime_[".qt"] = "video/quicktime";
  107. mime_[".avi"] = "video/x-msvideo";
  108. mime_[".asf"] = "video/x-ms-asf";
  109. mime_[".asx"] = "video/x-ms-asf";
  110. mime_[".wmv"] = "video/x-ms-wmv";
  111. mime_[".bz2"] = "application/x-bzip";
  112. mime_[".tbz"] = "application/x-bzip-compressed-tar";
  113. }
  114. else {
  115. load_mime_types(mime_file);
  116. }
  117. }
  118. void file_server::load_mime_types(std::string file_name)
  119. {
  120. booster::nowide::ifstream inp(file_name.c_str());
  121. if(!inp) {
  122. return;
  123. }
  124. std::string line;
  125. while(!inp.eof() && getline(inp,line)) {
  126. if(line.empty() || line[0]=='#')
  127. continue;
  128. std::istringstream ss(line);
  129. std::string mime;
  130. std::string ext;
  131. if(ss>>mime) {
  132. while(ss>>ext) {
  133. mime_["."+ext]=mime;
  134. }
  135. }
  136. }
  137. }
  138. file_server::~file_server()
  139. {
  140. }
  141. bool file_server::canonical(std::string normal,std::string &real)
  142. {
  143. #ifndef CPPCMS_WIN_NATIVE
  144. #ifdef CPPCMS_HAVE_CANONICALIZE_FILE_NAME
  145. char *canon=::canonicalize_file_name(normal.c_str());
  146. if(!canon) return false;
  147. try {
  148. real=canon;
  149. }
  150. catch(...)
  151. {
  152. free(canon);
  153. throw;
  154. }
  155. free(canon);
  156. canon=0;
  157. #else
  158. #if defined(PATH_MAX)
  159. int len = PATH_MAX;
  160. #else
  161. int len = pathconf(normal.c_str(),_PC_PATH_MAX);
  162. if(len <= 0)
  163. len = 32768; // Hope it is enough
  164. #endif
  165. std::vector<char> buffer;
  166. try {
  167. // Size may be not feasible for allocation according to POSIX
  168. buffer.resize(len,0);
  169. }
  170. catch(std::bad_alloc const &e) {
  171. buffer.resize(32768);
  172. }
  173. char *canon = ::realpath(normal.c_str(),&buffer.front());
  174. if(!canon)
  175. return false;
  176. real = canon;
  177. #endif
  178. #else
  179. wchar_t *wreal = 0;
  180. try {
  181. std::wstring wnormal = booster::locale::conv::utf_to_utf<wchar_t>(normal,booster::locale::conv::stop);
  182. wchar_t *wreal = _wfullpath(0,wnormal.c_str(),0);
  183. if(!wreal)
  184. return false;
  185. real = booster::locale::conv::utf_to_utf<char>(wreal,booster::locale::conv::stop);
  186. free(wreal);
  187. wreal = 0;
  188. }
  189. catch(booster::locale::conv::conversion_error const &) {
  190. if(wreal)
  191. free(wreal);
  192. return false;
  193. }
  194. // stat would not work on files like foo/ so remove the last slash as realpath
  195. // and canonicalize does
  196. if(real.size()>1 && real[real.size()-1]=='\\')
  197. real.resize(real.size()-1);
  198. #endif
  199. return true;
  200. }
  201. static bool is_directory_separator(char c)
  202. {
  203. #ifdef CPPCMS_WIN32
  204. return c=='\\' || c=='/';
  205. #else
  206. return c=='/';
  207. #endif
  208. }
  209. static bool is_file_prefix(std::string const &prefix,std::string const &full)
  210. {
  211. size_t prefix_size = prefix.size();
  212. if(prefix_size > full.size())
  213. return false;
  214. if(memcmp(prefix.c_str(),full.c_str(),prefix_size) != 0)
  215. return false;
  216. if(prefix_size == 0 || is_directory_separator(prefix[prefix_size-1]))
  217. return true;
  218. if(full.size() > prefix_size && !is_directory_separator(full[prefix_size]))
  219. return false;
  220. return true;
  221. }
  222. bool file_server::is_in_root(std::string const &input_path,std::string const &root,std::string &real)
  223. {
  224. std::string normal=root + "/" + input_path;
  225. if(!canonical(normal,real))
  226. return false;
  227. if(!is_file_prefix(root,real))
  228. return false;
  229. return true;
  230. }
  231. void file_server::normalize_path(std::string &path)
  232. {
  233. #ifdef CPPCMS_WIN32
  234. for(size_t i=0;i<path.size();i++)
  235. if(path[i]=='\\')
  236. path[i]='/';
  237. #endif
  238. if(path.empty() || path[0]!='/')
  239. path = "/" + path;
  240. std::string::iterator out = path.begin() + 1;
  241. std::string::iterator start = path.begin() + 1;
  242. while(start < path.end()) {
  243. std::string::iterator end = std::find(start,path.end(),'/');
  244. if(end==start || (end-start == 1 && *start == '.')) { // case of "//" and "/./"
  245. // nothing to do
  246. }
  247. else if(end-start == 2 && *start == '.' && *(start+1) == '.') {
  248. std::string::iterator min_pos = path.begin() + 1;
  249. if(out > min_pos)
  250. out --;
  251. while(out > min_pos) {
  252. out --;
  253. if(*out == '/')
  254. break;
  255. }
  256. }
  257. else {
  258. out = std::copy(start,end,out);
  259. if(end != path.end())
  260. *out++ = '/';
  261. }
  262. if(end == path.end())
  263. break;
  264. start = end;
  265. ++start;
  266. }
  267. if(*(out-1) == '/' && out > path.begin()+1)
  268. out--;
  269. path.resize(out - path.begin());
  270. }
  271. bool file_server::check_in_document_root(std::string normal,std::string &real)
  272. {
  273. normalize_path(normal);
  274. std::string root = document_root_;
  275. for(unsigned i=0;i<alias_.size();i++) {
  276. std::string const &ref=alias_[i].first;
  277. if(is_file_prefix(ref,normal))
  278. {
  279. root = alias_[i].second;
  280. normal = normal.substr(ref.size());
  281. if(normal.empty())
  282. normal="/";
  283. break;
  284. }
  285. }
  286. if(normal.empty())
  287. return false;
  288. if(normal[0]!='/')
  289. return false;
  290. if(check_symlinks_) {
  291. if(!is_in_root(normal,root,real))
  292. return false;
  293. }
  294. else {
  295. real = root + normal;
  296. }
  297. return true;
  298. }
  299. namespace {
  300. #ifdef CPPCMS_WIN_NATIVE
  301. typedef struct _stat port_stat;
  302. int get_stat(char const *name,port_stat *st)
  303. {
  304. std::wstring wname = booster::locale::conv::utf_to_utf<wchar_t>(name,booster::locale::conv::stop);
  305. return ::_wstat(wname.c_str(),st);
  306. }
  307. #else
  308. typedef struct stat port_stat;
  309. int get_stat(char const *name,port_stat *st)
  310. {
  311. return ::stat(name,st);
  312. }
  313. #endif
  314. }
  315. int file_server::file_mode(std::string const &file_name)
  316. {
  317. port_stat st;
  318. if(get_stat(file_name.c_str(),&st) < 0)
  319. return 0;
  320. return st.st_mode;
  321. }
  322. void file_server::list_dir(std::string const &url,std::string const &path)
  323. {
  324. cppcms::impl::directory d;
  325. if(!d.open(path)) {
  326. show404();
  327. return;
  328. }
  329. #ifdef CPPCMS_WIN_NATIVE
  330. response().content_type("text/html; charset=UTF-8");
  331. #endif
  332. std::ostream &out = response().out();
  333. out << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
  334. " \"http://www.w3.org/TR/html4/loose.dtd\">\n";
  335. out << "<html><head><title>Directory Listing</title></head>\n"
  336. "<body><h1>Index of " << util::escape(url) << "</h1>\n";
  337. out << booster::locale::as::gmt;
  338. //out <<"<table cellpadding='0' cellspacing='2' border='0' >\n";
  339. out <<"<table>\n";
  340. out <<"<thead><tr><td width='60%'>File</td><td width='20%' >Date</td><td width='5%'>&nbsp;</td><td width='15%'>Size</td></tr></thead>\n"
  341. "<tbody>\n";
  342. if(url!="/" && !url.empty()) {
  343. out << "<tr><td><code><a href='../' >..</a></code></td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr>\n";
  344. }
  345. out << booster::locale::as::ftime("%Y-%m-%d %H:%M:%S");
  346. while(d.next()) {
  347. if(memcmp(d.name(),".",1) == 0)
  348. continue;
  349. port_stat st;
  350. if(get_stat((path + "/" + d.name()).c_str(),&st) < 0)
  351. continue;
  352. char const *add="";
  353. if(st.st_mode & S_IFDIR)
  354. add="/";
  355. else if(st.st_mode & S_IFREG)
  356. ;
  357. else
  358. continue;
  359. out << "<tr>";
  360. out << "<td><code><a href='"
  361. << util::urlencode(d.name()) << add << "'>" << util::escape(d.name()) << add << "</a></code></td>";
  362. out << "<td>" << booster::locale::as::strftime << st.st_mtime <<"</td><td>&nbsp;</td>";
  363. if(st.st_mode & S_IFREG)
  364. out << "<td>" << booster::locale::as::number << st.st_size <<"</td>";
  365. else
  366. out << "<td> <strong>-</strong> </td>";
  367. out <<"</tr>\n";
  368. }
  369. out <<"</tbody>\n</table>\n";
  370. out <<"<p>CppCMS-Embedded/" CPPCMS_PACKAGE_VERSION "</p>\n";
  371. out <<"</body>\n";
  372. }
  373. namespace file_server_detail {
  374. class async_file_handler : public booster::callable<void(cppcms::http::context::completion_type)>
  375. {
  376. public:
  377. async_file_handler(std::string const &path,booster::shared_ptr<cppcms::http::context> c) :
  378. f(path.c_str(),std::ios_base::binary),
  379. ctx(c)
  380. {
  381. }
  382. typedef booster::intrusive_ptr<async_file_handler> pointer_type;
  383. void go()
  384. {
  385. if(!f) {
  386. ctx->response().set_html_header();
  387. ctx->response().make_error_response(404);
  388. ctx->async_complete_response();
  389. }
  390. else {
  391. ctx->response();
  392. (*this)(cppcms::http::context::operation_completed);
  393. }
  394. }
  395. void operator()(cppcms::http::context::completion_type c)
  396. {
  397. if(c!=cppcms::http::context::operation_completed)
  398. return;
  399. char buf[4096];
  400. size_t total = 0;
  401. while(!f.eof() && total < 65536) {
  402. f.read(buf,sizeof(buf));
  403. size_t n = f.gcount();
  404. total += n;
  405. ctx->response().out().write(buf,n);
  406. }
  407. if(f.eof())
  408. ctx->async_complete_response();
  409. else
  410. ctx->async_flush_output(pointer_type(this));
  411. }
  412. private:
  413. booster::nowide::ifstream f;
  414. booster::shared_ptr<cppcms::http::context> ctx;
  415. };
  416. } // file_server_detail
  417. void file_server::main(std::string file_name)
  418. {
  419. std::string path;
  420. if(!check_in_document_root(file_name,path)) {
  421. show404();
  422. return;
  423. }
  424. int s=file_mode(path);
  425. if((s & S_IFDIR)) {
  426. std::string path2;
  427. int mode_2=0;
  428. bool have_index = check_in_document_root(file_name+"/" + index_file_ ,path2);
  429. if(have_index) {
  430. mode_2 = file_mode(path2);
  431. have_index = (mode_2 & S_IFREG) != 0;
  432. }
  433. if( !file_name.empty()
  434. && file_name[file_name.size()-1]!='/' // not ending with "/" as should
  435. && (have_index || list_directories_)
  436. )
  437. {
  438. response().set_redirect_header(file_name + "/");
  439. response().out()<<std::flush;
  440. return;
  441. }
  442. if(have_index) {
  443. path = path2;
  444. s=mode_2;
  445. }
  446. else {
  447. if(list_directories_)
  448. list_dir(file_name,path);
  449. else
  450. show404();
  451. return;
  452. }
  453. }
  454. if(!(s & S_IFREG)) {
  455. show404();
  456. return;
  457. }
  458. std::string ext;
  459. size_t pos = path.rfind('.');
  460. if(pos != std::string::npos)
  461. ext=path.substr(pos);
  462. mime_type::const_iterator p=mime_.find(ext);
  463. if(p!=mime_.end())
  464. response().content_type(p->second);
  465. else
  466. response().content_type("application/octet-stream");
  467. if(!allow_deflate_ && !async_) {
  468. response().io_mode(http::response::nogzip);
  469. }
  470. if(async_) {
  471. file_server_detail::async_file_handler::pointer_type p=new file_server_detail::async_file_handler(path,release_context());
  472. p->go();
  473. }
  474. else {
  475. booster::nowide::ifstream file(path.c_str(),std::ios_base::binary);
  476. if(!file) {
  477. show404();
  478. return;
  479. }
  480. response().out()<<file.rdbuf(); // write stream to stream
  481. }
  482. }
  483. void file_server::show404()
  484. {
  485. response().set_html_header();
  486. response().make_error_response(404);
  487. }
  488. } // impl
  489. } // cppcms