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.
 
 
 
 
 
 

271 lines
6.4 KiB

  1. #define CPPCMS_SOURCE
  2. #include "config.h"
  3. # if defined(HAVE_CANONICALIZE_FILE_NAME) && !defined(_GNU_SOURCE)
  4. # define _GNU_SOURCE
  5. #endif
  6. #include <stdlib.h>
  7. #include "application.h"
  8. #include "service.h"
  9. #include "http_response.h"
  10. #include "internal_file_server.h"
  11. #include "cppcms_error.h"
  12. #include "json.h"
  13. #include "util.h"
  14. #include <sstream>
  15. #include <fstream>
  16. #include <string.h>
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #ifdef CPPCMS_WIN_NATIVE
  20. #include <windows.h>
  21. #else
  22. #include <unistd.h>
  23. #include <limits.h>
  24. #endif
  25. namespace cppcms {
  26. namespace impl {
  27. file_server::file_server(cppcms::service &srv) : application(srv)
  28. {
  29. if(!canonical(settings().get("file_server.document_root","."),document_root_))
  30. throw cppcms_error("Invalid document root");
  31. std::string mime_file=settings().get("file_server.mime_types","");
  32. if(mime_file.empty()) {
  33. mime_[".pdf"] = "application/pdf";
  34. mime_[".sig"] = "application/pgp-signature";
  35. mime_[".spl"] = "application/futuresplash";
  36. mime_[".ps"] = "application/postscript";
  37. mime_[".torrent"]= "application/x-bittorrent";
  38. mime_[".dvi"] = "application/x-dvi";
  39. mime_[".gz"] = "application/x-gzip";
  40. mime_[".pac"] = "application/x-ns-proxy-autoconfig";
  41. mime_[".swf"] = "application/x-shockwave-flash";
  42. mime_[".tgz"] = "application/x-tgz";
  43. mime_[".tar"] = "application/x-tar";
  44. mime_[".zip"] = "application/zip";
  45. mime_[".mp3"] = "audio/mpeg";
  46. mime_[".m3u"] = "audio/x-mpegurl";
  47. mime_[".wma"] = "audio/x-ms-wma";
  48. mime_[".wax"] = "audio/x-ms-wax";
  49. mime_[".ogg"] = "application/ogg";
  50. mime_[".wav"] = "audio/x-wav";
  51. mime_[".gif"] = "image/gif";
  52. mime_[".jpg"] = "image/jpeg";
  53. mime_[".jpeg"] = "image/jpeg";
  54. mime_[".png"] = "image/png";
  55. mime_[".xbm"] = "image/x-xbitmap";
  56. mime_[".xpm"] = "image/x-xpixmap";
  57. mime_[".xwd"] = "image/x-xwindowdump";
  58. mime_[".css"] = "text/css";
  59. mime_[".html"] = "text/html";
  60. mime_[".htm"] = "text/html";
  61. mime_[".js"] = "text/javascript";
  62. mime_[".asc"] = "text/plain";
  63. mime_[".c"] = "text/plain";
  64. mime_[".cpp"] = "text/plain";
  65. mime_[".log"] = "text/plain";
  66. mime_[".conf"] = "text/plain";
  67. mime_[".text"] = "text/plain";
  68. mime_[".txt"] = "text/plain";
  69. mime_[".dtd"] = "text/xml";
  70. mime_[".xml"] = "text/xml";
  71. mime_[".mpeg"] = "video/mpeg";
  72. mime_[".mpg"] = "video/mpeg";
  73. mime_[".mov"] = "video/quicktime";
  74. mime_[".qt"] = "video/quicktime";
  75. mime_[".avi"] = "video/x-msvideo";
  76. mime_[".asf"] = "video/x-ms-asf";
  77. mime_[".asx"] = "video/x-ms-asf";
  78. mime_[".wmv"] = "video/x-ms-wmv";
  79. mime_[".bz2"] = "application/x-bzip";
  80. mime_[".tbz"] = "application/x-bzip-compressed-tar";
  81. }
  82. else {
  83. load_mime_types(mime_file);
  84. }
  85. }
  86. void file_server::load_mime_types(std::string file_name)
  87. {
  88. std::ifstream inp(file_name.c_str());
  89. if(!inp) {
  90. return;
  91. }
  92. std::string line;
  93. while(!inp.eof() && getline(inp,line)) {
  94. if(line.empty() || line[0]=='#')
  95. continue;
  96. std::istringstream ss(line);
  97. std::string mime;
  98. std::string ext;
  99. if(ss>>mime) {
  100. while(ss>>ext) {
  101. mime_["."+ext]=mime;
  102. }
  103. }
  104. }
  105. }
  106. file_server::~file_server()
  107. {
  108. }
  109. bool file_server::canonical(std::string normal,std::string &real)
  110. {
  111. #ifndef CPPCMS_WIN_NATIVE
  112. #ifdef HAVE_CANONICALIZE_FILE_NAME
  113. char *canon=::canonicalize_file_name(normal.c_str());
  114. if(!canon) return false;
  115. real=canon;
  116. free(canon);
  117. canon=0;
  118. #else
  119. #if defined(PATH_MAX)
  120. int len = PATH_MAX;
  121. #else
  122. int len = pathconf(normal.c_str(),_PC_PATH_MAX);
  123. if(len <= 0)
  124. len = 32768; // Hope it is enough
  125. #endif
  126. std::vector<char> buffer;
  127. try {
  128. // Size may be not feasible for allocation according to POSIX
  129. buffer.resize(len,0);
  130. }
  131. catch(std::bad_alloc const &e) {
  132. buffer.resize(32768);
  133. }
  134. char *canon = ::realpath(normal.c_str(),&buffer.front());
  135. if(!canon)
  136. return false;
  137. real = canon;
  138. #endif
  139. #else
  140. int size=4096;
  141. std::vector<char> buffer(size,0);
  142. for(;;) {
  143. DWORD res = ::GetFullPathName(normal.c_str(),buffer.size(),&buffer.front(),0);
  144. if(res == 0)
  145. return false;
  146. if(res >= buffer.size()) {
  147. buffer.resize(buffer.size()*2,0);
  148. }
  149. else {
  150. real=&buffer.front();
  151. break;
  152. }
  153. }
  154. #endif
  155. return true;
  156. }
  157. bool file_server::check_in_document_root(std::string normal,std::string &real)
  158. {
  159. normal=document_root_ + "/" + normal;
  160. if(!canonical(normal,real))
  161. return false;
  162. if(real.size() < document_root_.size() || memcmp(real.c_str(),document_root_.c_str(),document_root_.size()) !=0)
  163. return false;
  164. return true;
  165. }
  166. int file_server::file_mode(std::string const &file_name)
  167. {
  168. struct stat st;
  169. if(::stat(file_name.c_str(),&st) < 0)
  170. return 0;
  171. return st.st_mode;
  172. }
  173. void file_server::main(std::string file_name)
  174. {
  175. file_name = util::urldecode(file_name);
  176. if(file_name.empty() || file_name[file_name.size()-1]=='/')
  177. file_name+="/index.html";
  178. std::string path;
  179. if(!check_in_document_root(file_name,path)) {
  180. show404();
  181. return;
  182. }
  183. int s=file_mode(path);
  184. if((s & S_IFDIR) && (file_mode(path+"/index.html") & S_IFREG)) {
  185. response().set_redirect_header(file_name + "/");
  186. response().out()<<std::flush;
  187. return;
  188. }
  189. if(!(s & S_IFREG)) {
  190. show404();
  191. return;
  192. }
  193. std::string ext;
  194. size_t pos = path.rfind('.');
  195. if(pos != std::string::npos)
  196. ext=path.substr(pos);
  197. mime_type::const_iterator p=mime_.find(ext);
  198. if(p!=mime_.end())
  199. response().content_type(p->second);
  200. else
  201. response().content_type("application/octet-stream");
  202. if(!settings().get("http.allow_deflate",false)) {
  203. response().io_mode(http::response::nogzip);
  204. }
  205. std::ifstream file(path.c_str(),std::ifstream::binary | std::ifstream::in);
  206. if(!file) {
  207. show404();
  208. return;
  209. }
  210. std::vector<char> buffer(4096);
  211. while(!file.eof()) {
  212. file.read(&buffer.front(),buffer.size());
  213. response().out().write(&buffer.front(),file.gcount());
  214. }
  215. response().out()<<std::flush;
  216. }
  217. void file_server::show404()
  218. {
  219. response().status(http::response::not_found);
  220. response().set_html_header();
  221. response().out() <<
  222. "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
  223. " \"http://www.w3.org/TR/html4/loose.dtd\">\n"
  224. "<html>\n"
  225. " <head>\n"
  226. " <title>404 Not Found</title>\n"
  227. " </head>\n"
  228. " <body>\n"
  229. " <h1>404 Not Found</h1>\n"
  230. " </body>\n"
  231. "</html>\n"<<std::flush;
  232. }
  233. } // impl
  234. } // cppcms