PageRenderTime 59ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://github.com/darrengarvey/cgi
C++ | 322 lines | 238 code | 20 blank | 64 comment | 105 complexity | 46db5dbdbf07eab60743f0bd390668bf 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 <istream>
  16. #include <fstream>
  17. #include <boost/filesystem.hpp>
  18. #include <boost/shared_array.hpp>
  19. #include <boost/algorithm/string/find.hpp>
  20. #include <boost/algorithm/string/case_conv.hpp>
  21. ///////////////////////////////////////////////////////////
  22. #include "boost/cgi/cgi.hpp"
  23. using std::cerr;
  24. using std::endl;
  25. using std::ifstream;
  26. using namespace boost::cgi;
  27. namespace fs = boost::filesystem;
  28. namespace algo = boost::algorithm;
  29. /// Get the MIME type of the file.
  30. /**
  31. * @returns The MIME type as a string. Returns an empty string if the
  32. * file type isn't recognised / supported.
  33. */
  34. std::string get_mime_type(fs::path const& file)
  35. {
  36. fs::path ext = file.extension();
  37. if (ext.empty())
  38. return "";
  39. // Note: we want the string after the '.'
  40. std::size_t pos(ext.string().rfind("."));
  41. std::string filetype (ext.string().substr(pos));
  42. algo::to_lower(filetype);
  43. /// Ordinary text files.
  44. if (filetype == "ini" || filetype == "txt" || filetype == "conf")
  45. return "text/plain";
  46. else
  47. if (filetype == "js")
  48. return "application/javascript";
  49. else
  50. if (filetype == "json")
  51. return "application/json";
  52. else
  53. if (filetype == "css")
  54. return "text/css";
  55. else
  56. /// Structured text files.
  57. if (filetype == "html" || filetype == "htm")
  58. return "text/html";
  59. else
  60. if (filetype == "xml")
  61. return "text/xml";
  62. else
  63. if (filetype == "csv")
  64. return "text/csv";
  65. else
  66. if (filetype == "rtf")
  67. return "text/rtf";
  68. else
  69. /// Image files.
  70. if (filetype == "jpg" || filetype == "jpeg")
  71. return "image/jpeg";
  72. else
  73. if (filetype == "gif")
  74. return "image/gif";
  75. else
  76. if (filetype == "bmp")
  77. return "image/x-ms-bmp";
  78. else
  79. if (filetype == "png")
  80. return "image/png";
  81. else
  82. if (filetype == "tiff")
  83. return "image/tiff";
  84. else
  85. /// Audio files.
  86. if (filetype == "ogg")
  87. return "audio/ogg";
  88. else
  89. if (filetype == "mp3")
  90. return "audio/mpeg";
  91. else
  92. /// Video files.
  93. if (filetype == "avi")
  94. return "video/x-msvideo";
  95. else
  96. /// Rich media files.
  97. if (filetype == "pdf")
  98. return "application/pdf";
  99. else
  100. if (filetype == "doc")
  101. return "application/msword";
  102. else
  103. if (filetype == "swf")
  104. return "application/x-shockwave-flash";
  105. else
  106. if (filetype == "xls")
  107. return "application/vnd.ms-excel";
  108. /// Compressed files.
  109. else
  110. if (filetype == "zip")
  111. return "application/zip";
  112. else
  113. if (filetype == "tar")
  114. return "application/x-tar";
  115. /// Other files.
  116. else
  117. if (filetype == "pl")
  118. return "application/x-perl";
  119. else
  120. if (filetype == "py")
  121. return "application/x-python";
  122. else
  123. if (filetype == "exe" || filetype == "dll" || filetype == "sys" ||
  124. filetype == "chm" || filetype == "lib" || filetype == "pdb" ||
  125. filetype == "obj" || filetype == "dep" || filetype == "idb" ||
  126. filetype == "pyd" || filetype == "sqm" || filetype == "idb" ||
  127. filetype == "asm" || filetype == "suo" || filetype == "sbr")
  128. return ""; // Not allowed to download these file types.
  129. return "text/plain";
  130. }
  131. /// Show the file to the user.
  132. /**
  133. * This will send the file to the user using an appropriate content-type
  134. * header. Some browsers (eg. Firefox) will be able to handle many file types
  135. * directly, while others (eg. Internet Explorer) will prompt the user to
  136. * save / open the file externally.
  137. *
  138. * This function actually buffers the entire file before sending it to the user,
  139. * so this isn't very scalable. You could choose to stream the file directly
  140. * instead, but make sure your HTTP server won't buffer the response.
  141. *
  142. * Files over 200MB won't be displayed.
  143. */
  144. template<typename Response, typename Client>
  145. void show_file(Response& resp, Client& client, fs::path const& file)
  146. {
  147. if (!fs::exists(file))
  148. resp<< "File not found.";
  149. else
  150. {
  151. boost::uintmax_t size (fs::file_size(file));
  152. if (size > 200000000L) // Files must be < 200MB
  153. resp<< "File too large: " << file.string() << " [" << size << ']';
  154. else
  155. {
  156. /// Check the file type is allowed.
  157. std::string mime_type (get_mime_type(file));
  158. if (!mime_type.empty())
  159. {
  160. std::string ctype (content_type(mime_type).content + "\r\n\r\n");
  161. /// Open the file and read it as binary data.
  162. ifstream ifs (file.string().c_str(), std::ios::binary);
  163. if (ifs.is_open())
  164. {
  165. resp<< content_type(mime_type);
  166. boost::uintmax_t bufsize = 500;
  167. boost::uintmax_t read_bytes;
  168. char buf[500];
  169. ifs.seekg(0, std::ios::beg);
  170. while (!ifs.eof() && size > 0)
  171. {
  172. ifs.read(buf, size < bufsize ? size : bufsize);
  173. read_bytes = ifs.gcount();
  174. size -= read_bytes;
  175. resp.write(buf, read_bytes);
  176. }
  177. }
  178. }
  179. else
  180. resp<< "File type not allowed.";
  181. }
  182. }
  183. }
  184. template<typename Response>
  185. void show_paths(Response& resp, fs::path const& parent, bool recursive = true)
  186. {
  187. if (!fs::exists(parent))
  188. {
  189. resp<< "File does not exist\n";
  190. return;
  191. }
  192. resp<< "<ul>";
  193. if (fs::is_directory(parent))
  194. {
  195. resp<< parent.string() << "\n";
  196. resp<< "<li class=\"directory\"><a href=\"?dir="
  197. << parent.string() << "\">.</a></li>\n";
  198. if (fs::is_directory(parent.parent_path()))
  199. {
  200. resp<< "<li class=\"directory\"><a href=\"?dir="
  201. << parent.parent_path().string() << "\">..</a></li>\n";
  202. }
  203. boost::system::error_code ec0, ec1;
  204. for (fs::directory_iterator iter(parent, ec0), end; iter != end; ++iter)
  205. {
  206. const bool is_dir = fs::is_directory(*iter, ec1);
  207. if (ec0.value() || ec1.value())
  208. {
  209. resp << "<li>" << iter->path().filename().string()
  210. << " ("
  211. << (ec0.value() ? ec0.message() : ec1.message())
  212. << ")</li>\n";
  213. }
  214. else if (is_dir)
  215. {
  216. resp<< "<li class=\"directory\"><a href=\"?dir="
  217. << iter->path().string() << "\">"
  218. << iter->path().filename().string()
  219. << iter->path().preferred_separator << "</a></li>\n";
  220. if (recursive && !fs::is_symlink(*iter))
  221. show_paths(resp, iter->path(), recursive);
  222. }
  223. else
  224. {
  225. // display filename only.
  226. resp<< "<li class=\"file\"><a href=\"?file="
  227. << iter->path().string() << "\">"
  228. << iter->path().filename().string()
  229. << "</a>";
  230. //if (fs::is_regular_file(iter->status()))
  231. // resp<< " [" << fs::file_size(iter->path()) << " bytes]";
  232. resp<< "</li>\n";
  233. }
  234. }
  235. }
  236. else
  237. {
  238. resp<< "<li class=\"file\">" << "<a href=\"?file="
  239. << parent.string() << "\">" << parent << "</li>\n";
  240. }
  241. resp<< "</ul>";
  242. }
  243. /// This function accepts and handles a single request.
  244. template<typename Request>
  245. int handle_request(Request& req)
  246. {
  247. boost::system::error_code ec;
  248. //
  249. // Load in the request data so we can access it easily.
  250. //
  251. req.load(parse_all); // Read and parse STDIN (ie. POST) data.
  252. //
  253. // Construct a `response` object (makes writing/sending responses easier).
  254. //
  255. response resp;
  256. if (req.get.count("file"))
  257. {
  258. show_file(resp, req.client(), req.get["file"]);
  259. //return req.close(http::ok, 0);
  260. }
  261. else
  262. if (req.get.count("dir"))
  263. {
  264. //
  265. // Responses in CGI programs require at least a 'Content-type' header.
  266. // The library provides helpers for several common headers:
  267. //
  268. resp<< content_type("text/html");
  269. // You can also stream text to a response.
  270. // All of this just prints out the form
  271. resp<< "<html>"
  272. "<head><title>CGI File Browser Example</title><head>"
  273. "<body>";
  274. show_paths(resp, req.get["dir"], req.get["recurse"] == "1");
  275. resp<< "</body></html>";
  276. }
  277. else
  278. resp<< content_type("text/html")
  279. << "No path specified. Search for a path using, eg. "
  280. << " <a href=\"?dir=/\">" << req.script_name() << "?dir=/some/path</a>\n";
  281. resp<< header("CGI-client", "fcgi_file_browser");
  282. return commit(req, resp);
  283. }
  284. int main()
  285. {
  286. try {
  287. request req;
  288. return handle_request(req);
  289. }catch(boost::system::system_error const& se){
  290. // This is the type of error thrown by the library.
  291. cerr<< "[fcgi] System error: " << se.what() << endl;
  292. return -1;
  293. }catch(std::exception const& e){
  294. // Catch any other exceptions
  295. cerr<< "[fcgi] Exception: " << e.what() << endl;
  296. return -1;
  297. }catch(...){
  298. cerr<< "[fcgi] Uncaught exception!" << endl;
  299. return -1;
  300. }
  301. }
  302. //]