PageRenderTime 71ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/libs/cgi/example/fcgi/file_browser/main.cpp

http://github.com/darrengarvey/cgi
C++ | 370 lines | 277 code | 27 blank | 66 comment | 110 complexity | c19c0e8a0ff24cb1d8b88a98316f5e21 MD5 | raw file
  1. // -- main.hpp --
  2. //
  3. // Copyright (c) Darren Garvey 2009.
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. ////////////////////////////////////////////////////////////////
  9. //
  10. //[fcgi_file_browser
  11. //
  12. // This example is a simple browser-based file browser.
  13. //
  14. ///////////////////////////////////////////////////////////
  15. #include <fstream>
  16. #include <boost/filesystem.hpp>
  17. #include <boost/algorithm/string/find.hpp>
  18. #include <boost/algorithm/string/case_conv.hpp>
  19. ///////////////////////////////////////////////////////////
  20. #include "boost/cgi/fcgi.hpp"
  21. using std::cerr;
  22. using std::endl;
  23. using std::ifstream;
  24. using namespace boost::fcgi;
  25. namespace fs = boost::filesystem;
  26. namespace algo = boost::algorithm;
  27. std::size_t process_id()
  28. {
  29. #if defined(BOOST_WINDOWS)
  30. return _getpid();
  31. #else
  32. return getpid();
  33. #endif
  34. }
  35. /// Get the MIME type of the file.
  36. /**
  37. * @returns The MIME type as a string. Returns an empty string if the
  38. * file type isn't recognised / supported.
  39. */
  40. std::string get_mime_type(fs::path const& file)
  41. {
  42. fs::path ext = file.extension();
  43. if (ext.empty())
  44. return "";
  45. // Note: we want the string after the '.'
  46. std::size_t pos(ext.string().rfind("."));
  47. std::string filetype (ext.string().substr(pos));
  48. algo::to_lower(filetype);
  49. /// Ordinary text files.
  50. if (filetype == "ini" || filetype == "txt" || filetype == "conf")
  51. return "text/plain";
  52. else
  53. if (filetype == "js")
  54. return "application/javascript";
  55. else
  56. if (filetype == "json")
  57. return "application/json";
  58. else
  59. if (filetype == "css")
  60. return "text/css";
  61. else
  62. /// Structured text files.
  63. if (filetype == "html" || filetype == "htm")
  64. return "text/html";
  65. else
  66. if (filetype == "xml")
  67. return "text/xml";
  68. else
  69. if (filetype == "csv")
  70. return "text/csv";
  71. else
  72. if (filetype == "rtf")
  73. return "text/rtf";
  74. else
  75. /// Image files.
  76. if (filetype == "jpg" || filetype == "jpeg")
  77. return "image/jpeg";
  78. else
  79. if (filetype == "gif")
  80. return "image/gif";
  81. else
  82. if (filetype == "bmp")
  83. return "image/x-ms-bmp";
  84. else
  85. if (filetype == "png")
  86. return "image/png";
  87. else
  88. if (filetype == "tiff")
  89. return "image/tiff";
  90. else
  91. /// Audio files.
  92. if (filetype == "ogg")
  93. return "audio/ogg";
  94. else
  95. if (filetype == "flac")
  96. return "audio/flac";
  97. else
  98. if (filetype == "mp3")
  99. return "audio/mpeg";
  100. else
  101. /// Video files.
  102. if (filetype == "avi")
  103. return "video/x-msvideo";
  104. else
  105. if (filetype == "wmv")
  106. return "video/x-ms-wmv";
  107. else
  108. /// Rich media files.
  109. if (filetype == "pdf")
  110. return "application/pdf";
  111. else
  112. if (filetype == "doc")
  113. return "application/msword";
  114. else
  115. if (filetype == "swf")
  116. return "application/x-shockwave-flash";
  117. else
  118. if (filetype == "xls")
  119. return "application/vnd.ms-excel";
  120. /// Compressed files.
  121. else
  122. if (filetype == "zip")
  123. return "application/zip";
  124. else
  125. if (filetype == "tar")
  126. return "application/x-tar";
  127. /// Other files.
  128. else
  129. if (filetype == "pl")
  130. return "application/x-perl";
  131. else
  132. if (filetype == "py")
  133. return "application/x-python";
  134. else
  135. if (filetype == "exe" || filetype == "dll" || filetype == "sys" ||
  136. filetype == "chm" || filetype == "lib" || filetype == "pdb" ||
  137. filetype == "obj" || filetype == "dep" || filetype == "idb" ||
  138. filetype == "pyd" || filetype == "sqm" || filetype == "idb" ||
  139. filetype == "asm" || filetype == "suo" || filetype == "sbr")
  140. return ""; // Not allowed to download these file types.
  141. return "text/plain";
  142. }
  143. template<typename Response, typename Client>
  144. boost::system::error_code& show_file(
  145. Response& resp, Client& client, fs::path const& file
  146. , boost::system::error_code& ec)
  147. {
  148. if (!fs::exists(file))
  149. resp<< content_type("text/plain")
  150. << "File not found.";
  151. else
  152. {
  153. boost::uintmax_t size (fs::file_size(file));
  154. cerr<< "size: " << size << endl;
  155. if (size > 750000000L) // Files must be < 750MB (this is arbitrary).
  156. resp<< "File too large.";
  157. else
  158. {
  159. /// Check the file type is allowed.
  160. std::string mime_type (get_mime_type(file));
  161. if (!mime_type.empty())
  162. {
  163. // Write unbuffered (ie. not using a response). This makes sense
  164. // for large files, which would take up too much memory to
  165. // buffer.
  166. // First, some debugging to the console.
  167. cerr<< "MIME-type: " << mime_type << '\n';
  168. cerr<< "File size: " << size << '\n';
  169. /// Open the file and read it as binary data.
  170. ifstream ifs (file.string().c_str(), std::ios::binary);
  171. if (ifs.is_open())
  172. {
  173. // Write out the response headers.
  174. std::string ctype (content_type(mime_type));
  175. std::string clen (content_length<char>(size));
  176. clen += "\r\n";
  177. // Create an output buffer. This will only hold references
  178. // to the data so we need to ensure the data stays in memory
  179. // until the write completes.
  180. //
  181. // The purpose of this buffer is so efficient I/O operations
  182. // (eg. scatter-gather) can be used under the hood. This vector
  183. // is light-weight so it can be copied cheaply.
  184. std::vector<boost::asio::const_buffer> output(3);
  185. output.push_back(boost::asio::buffer(ctype));
  186. output.push_back(boost::asio::buffer(clen));
  187. // Read then write up to 1MB at a time.
  188. boost::uintmax_t bufsize = 1000000;
  189. boost::uintmax_t read_bytes;
  190. char buf[1000000];
  191. ifs.seekg(0, std::ios::beg);
  192. while (!ifs.eof() && size > 0 && !ec)
  193. {
  194. ifs.read(buf, (std::streamsize)(size < bufsize ? size : bufsize));
  195. read_bytes = ifs.gcount();
  196. size -= read_bytes;
  197. output.push_back(boost::asio::buffer(buf, (std::size_t)read_bytes));
  198. // Write unbuffered (ie. not using a response).
  199. write(client, output
  200. , boost::asio::transfer_all(), ec);
  201. // This needs to go at the end, so the Content-type, etc.
  202. // headers are sent the first time around.
  203. output.clear();
  204. }
  205. }
  206. else
  207. {
  208. resp<< content_type("text/plain")
  209. << "File cannot be opened. Please try again later.";
  210. }
  211. }
  212. else
  213. {
  214. resp<< content_type("text/plain")
  215. << "File type not allowed.";
  216. }
  217. }
  218. }
  219. return ec;
  220. }
  221. template<typename Response>
  222. void show_paths(Response& resp, fs::path const& parent, bool recursive = true)
  223. {
  224. if (!fs::exists(parent))
  225. {
  226. resp<< "File does not exist\n";
  227. return;
  228. }
  229. resp<< "<ul>";
  230. if (fs::is_directory(parent))
  231. {
  232. resp<< parent << "\n";
  233. resp<< "<li class=\"directory\"><a href=\"?dir="
  234. << parent.string() << "\">.</a></li>\n";
  235. if (fs::is_directory(parent.parent_path()))
  236. resp<< "<li class=\"directory\"><a href=\"?dir="
  237. << parent.parent_path().string() << "\">..</a></li>\n";
  238. for (fs::directory_iterator iter(parent), end; iter != end; ++iter)
  239. {
  240. if (fs::is_directory(*iter))
  241. {
  242. resp<< "<li class=\"directory\"><a href=\"?dir="
  243. << iter->path() << "\">" << iter->path() << "</a></li>\n";
  244. if (recursive)
  245. show_paths(resp, iter->path(), recursive);
  246. }
  247. else
  248. {
  249. // Display only the filename.
  250. resp<< "<li class=\"file\"><a href=\"?file="
  251. << iter->path() << "\">" << iter->path().filename()
  252. << "</a>";
  253. resp<< "</li>\n";
  254. }
  255. }
  256. }
  257. else
  258. {
  259. resp<< "<li class=\"file\">" << "<a href=\"?file="
  260. << parent.string() << "\">" << parent << "</li>\n";
  261. }
  262. resp<< "</ul>";
  263. }
  264. /// This function accepts and handles a single request.
  265. template<typename Request>
  266. int handle_request(Request& req)
  267. {
  268. boost::system::error_code ec;
  269. //
  270. // Load in the request data so we can access it easily.
  271. //
  272. req.load(parse_all); // Read and parse STDIN (ie. POST) data.
  273. //
  274. // Construct a `response` object (makes writing/sending responses easier).
  275. //
  276. response resp;
  277. if (req.get.count("file"))
  278. {
  279. boost::system::error_code ec;
  280. show_file(resp, req.client(), req.get["file"], ec);
  281. if (ec)
  282. {
  283. cerr<< "Writing file finished unexpectedly!\n"
  284. " Error " << ec << ": " << ec.message() << '\n';
  285. // Carry on processing other requests.
  286. return req.close(http::request_timeout, -1, ec);
  287. }
  288. }
  289. else
  290. if (req.get.count("dir"))
  291. {
  292. //
  293. // Responses in CGI programs require at least a 'Content-type' header.
  294. // The library provides helpers for several common headers:
  295. //
  296. resp<< content_type("text/html");
  297. // You can also stream text to a response.
  298. // All of this just prints out the form
  299. resp<< "<html>"
  300. "<head><title>FastCGI File Browser Example</title><head>"
  301. "<body>";
  302. show_paths(resp, req.get["dir"], req.get["recurse"] == "1");
  303. resp<< "</body></html>";
  304. }
  305. else
  306. resp<< content_type("text/plain")
  307. << "No path specified.\n";
  308. resp<< header("FastCGI-client", "fcgi_file_browser");
  309. return commit(req, resp);
  310. }
  311. int main()
  312. {
  313. try {
  314. // Make a `service` (more about this in other examples).
  315. service s;
  316. acceptor a(s);
  317. int ret(0);
  318. for (;;)
  319. {
  320. request req(s);
  321. for (;;)
  322. {
  323. a.accept(req);
  324. if (handle_request(req))
  325. break;
  326. req.clear();
  327. }
  328. }
  329. return ret;
  330. }catch(boost::system::system_error const& se){
  331. // This is the type of error thrown by the library.
  332. cerr<< "[fcgi] System error: " << se.what() << endl;
  333. return -1;
  334. }catch(std::exception const& e){
  335. // Catch any other exceptions
  336. cerr<< "[fcgi] Exception: " << e.what() << endl;
  337. return -1;
  338. }catch(...){
  339. cerr<< "[fcgi] Uncaught exception!" << endl;
  340. return -1;
  341. }
  342. }
  343. //]