PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/libs/cgi/example/cgi/file_browser_debug/main.cpp

http://github.com/darrengarvey/cgi
C++ | 358 lines | 269 code | 24 blank | 65 comment | 107 complexity | 30d7b93b9f652c3e79027eed18de4bec 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. //[cgi_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/cgi.hpp"
  21. using std::cerr;
  22. using std::endl;
  23. using std::ifstream;
  24. using namespace boost::cgi;
  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\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(2);
  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[10000];
  191. ifs.seekg(0, std::ios::beg);
  192. cerr<< "Writing file contents now." << endl;
  193. while (!ifs.eof() && size > 0 && !ec)
  194. {
  195. std::streamsize num = (std::streamsize)(size < bufsize ? size : bufsize);
  196. cerr<< "num = " << num << endl;
  197. ifs.read(buf, num);
  198. read_bytes = ifs.gcount();
  199. cerr<< "Read " << read_bytes << " bytes (buf size: " << strlen(buf) << "." << endl;
  200. size -= read_bytes;
  201. output.push_back(boost::asio::buffer(buf, read_bytes));
  202. // Write unbuffered (ie. not using a response).
  203. write(client, output
  204. , boost::asio::transfer_all(), ec);
  205. cerr<< "Written (got: " << ec.message() << ")." << endl;
  206. // This needs to go at the end, so the Content-type, etc.
  207. // headers are sent the first time around.
  208. output.clear();
  209. }
  210. }
  211. else
  212. {
  213. resp<< content_type("text/plain")
  214. << "File cannot be opened. Please try again later.";
  215. }
  216. }
  217. else
  218. {
  219. resp<< content_type("text/plain")
  220. << "File type not allowed.";
  221. }
  222. }
  223. }
  224. return ec;
  225. }
  226. template<typename Response>
  227. void show_paths(Response& resp, fs::path const& parent, bool recursive = true)
  228. {
  229. if (!fs::exists(parent))
  230. {
  231. resp<< "File does not exist\n";
  232. return;
  233. }
  234. resp<< "<ul>";
  235. if (fs::is_directory(parent))
  236. {
  237. resp<< parent << "\n";
  238. resp<< "<li class=\"directory\"><a href=\"?dir="
  239. << parent.string() << "\">.</a></li>\n";
  240. if (fs::is_directory(parent.parent_path()))
  241. resp<< "<li class=\"directory\"><a href=\"?dir="
  242. << parent.parent_path().string() << "\">..</a></li>\n";
  243. for (fs::directory_iterator iter(parent), end; iter != end; ++iter)
  244. {
  245. if (fs::is_directory(*iter))
  246. {
  247. resp<< "<li class=\"directory\"><a href=\"?dir="
  248. << iter->path() << "\">" << iter->path() << "</a></li>\n";
  249. if (recursive)
  250. show_paths(resp, iter->path(), recursive);
  251. }
  252. else
  253. {
  254. // Display only the filename.
  255. resp<< "<li class=\"file\"><a href=\"?file="
  256. << iter->path() << "\">" << iter->path().filename()
  257. << "</a>";
  258. resp<< "</li>\n";
  259. }
  260. }
  261. }
  262. else
  263. {
  264. resp<< "<li class=\"file\">" << "<a href=\"?file="
  265. << parent.string() << "\">" << parent << "</li>\n";
  266. }
  267. resp<< "</ul>";
  268. }
  269. /// This function accepts and handles a single request.
  270. template<typename Request>
  271. int handle_request(Request& req)
  272. {
  273. boost::system::error_code ec;
  274. //
  275. // Load in the request data so we can access it easily.
  276. //
  277. req.load(parse_get); // Read and parse STDIN (ie. POST) data.
  278. //
  279. // Construct a `response` object (makes writing/sending responses easier).
  280. //
  281. response resp;
  282. if (req.get.count("file"))
  283. {
  284. boost::system::error_code ec;
  285. show_file(resp, req.client(), req.get["file"], ec);
  286. if (ec)
  287. {
  288. cerr<< "Writing file finished unexpectedly!\n"
  289. " Error " << ec << ": " << ec.message() << '\n';
  290. // Carry on processing other requests.
  291. return req.close(http::request_timeout, -1, ec);
  292. }
  293. }
  294. else
  295. if (req.get.count("dir"))
  296. {
  297. //
  298. // Responses in CGI programs require at least a 'Content-type' header.
  299. // The library provides helpers for several common headers:
  300. //
  301. resp<< content_type("text/html");
  302. // You can also stream text to a response.
  303. // All of this just prints out the form
  304. resp<< "<html>"
  305. "<head><title>CGI File Browser Example</title><head>"
  306. "<body>";
  307. show_paths(resp, req.get["dir"], req.get["recurse"] == "1");
  308. resp<< "</body></html>";
  309. }
  310. else
  311. resp<< content_type("text/plain")
  312. << "No path specified.\n";
  313. resp<< header("FastCGI-client", "fcgi_file_browser");
  314. return commit(req, resp);
  315. }
  316. int main()
  317. {
  318. try {
  319. request req;
  320. return handle_request(req);
  321. }catch(boost::system::system_error const& se){
  322. // This is the type of error thrown by the library.
  323. cerr<< "[cgi] System error: " << se.what() << endl;
  324. return -1;
  325. }catch(std::exception const& e){
  326. // Catch any other exceptions
  327. cerr<< "[cgi] Exception: " << e.what() << endl;
  328. return -1;
  329. }catch(...){
  330. cerr<< "[cgi] Uncaught exception!" << endl;
  331. return -1;
  332. }
  333. }
  334. //]