html.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. #include <sys/uio.h>
  2. #include "soh.h"
  3. #include "html.h"
  4. /******************************************
  5. This file contains functions for generating
  6. HTML pages and handling AJAX requests.
  7. ******************************************/
  8. /*
  9. " -> &#34;
  10. & -> &#38;
  11. ' -> &#39;
  12. < -> &#60;
  13. > -> &#62;
  14. \ -> &#92;
  15. */
  16. static int htmlSpecialChars( const char* str, char* buf, int bufSize ){
  17. int i;
  18. int res;
  19. int offset;
  20. offset = 0;
  21. for( i = 0; offset < bufSize; i++ ){
  22. if( str[i] != 34 && str[i] != 38 && str[i] != 39 && str[i] != 60 && str[i] != 62 && str[i] != 92 ){
  23. buf[offset] = str[i];
  24. if( !str[i] ) return offset;
  25. offset++;
  26. continue;
  27. }
  28. if( (offset + 6) >= bufSize ) return -2;
  29. res = sprintf( buf + offset, "&#%i;", str[i] );
  30. if( res != 5 ){
  31. perror( "htmlSpecialChars sprintf" );
  32. return -2;
  33. }
  34. offset += 5;
  35. }
  36. return -2;
  37. }
  38. int shareBrowserHtml( struct instance* this ){
  39. int i;
  40. int res;
  41. int contentLength;
  42. char sizeStr[32];
  43. int headersLength;
  44. char headers[1024];
  45. int pageLength;
  46. char page[65536];
  47. const char* pagePart1;
  48. const char* pagePart2;
  49. const char* pagePart3;
  50. size_t pagePart1Length;
  51. size_t pagePart2Length;
  52. size_t pagePart3Length;
  53. int jsconfigLength;
  54. char jsconfig[4096];
  55. char tmpbuf[4096];
  56. unsigned char hasFile;
  57. unsigned char hasRaw;
  58. unsigned char hasTar;
  59. struct iovec fullPage[6];
  60. ssize_t fullPageLen;
  61. //init some vars
  62. contentLength = 0;
  63. pageLength = 0;
  64. jsconfigLength = 0;
  65. hasFile = 0x00;
  66. hasRaw = 0x00;
  67. hasTar = 0x00;
  68. fullPageLen = 0;
  69. pagePart1 = _binary_page_part1_html_start;
  70. pagePart2 = _binary_page_part2_html_start;
  71. pagePart3 = _binary_page_part3_html_start;
  72. pagePart1Length = _binary_page_part1_html_end - _binary_page_part1_html_start;
  73. pagePart2Length = _binary_page_part2_html_end - _binary_page_part2_html_start;
  74. pagePart3Length = _binary_page_part3_html_end - _binary_page_part3_html_start;
  75. /********* avaliable utils *********/
  76. //TODO: use for()
  77. //check file avaliable
  78. for( i = 0; this->progs[i].name; i++ ){
  79. if( !this->progs[i].path ) continue;
  80. if( strcmp(this->progs[i].alias, "file") ){
  81. hasFile = 0xff;
  82. break;
  83. }
  84. }
  85. //check raw avaliable
  86. for( i = 0; this->progs[i].name; i++ ){
  87. if( !this->progs[i].path ) continue;
  88. if( strcmp(this->progs[i].alias, "raw") ){
  89. hasRaw = 0xff;
  90. break;
  91. }
  92. }
  93. //check tar avaliable
  94. for( i = 0; this->progs[i].name; i++ ){
  95. if( !this->progs[i].path ) continue;
  96. if( !strcmp(this->progs[i].alias, "tar") ){
  97. hasTar = 0xff;
  98. break;
  99. }
  100. }
  101. /********* js config *********/
  102. //var hasFile = true | false;
  103. res = sprintf( jsconfig + jsconfigLength, "var hasFile = %s;\n", hasFile ? "true" : "false" );
  104. if( res < 0 ) goto l_sprintf_error;
  105. jsconfigLength += res;
  106. //var hasRaw = true | false;
  107. res = sprintf( jsconfig + jsconfigLength, "var hasRaw = %s;\n", hasRaw ? "true" : "false" );
  108. if( res < 0 ) goto l_sprintf_error;
  109. jsconfigLength += res;
  110. //var hasTar = true | false;
  111. res = sprintf( jsconfig + jsconfigLength, "var hasTar = %s;\n", hasTar ? "true" : "false" );
  112. if( res < 0 ) goto l_sprintf_error;
  113. jsconfigLength += res;
  114. //var avaliableCompressors = [ "xz", "bz2", ..., null ];
  115. res = sprintf( jsconfig + jsconfigLength, "var avaliableCompressors = [ ");
  116. if( res < 0 ) goto l_sprintf_error;
  117. jsconfigLength += res;
  118. for( i = 0; this->progs[i].name; i++ ){
  119. if( !this->progs[i].path ) continue;
  120. if( this->progs[i].type == PROGDESC_TYPE_COMPRESSOR ){
  121. res = sprintf( jsconfig + jsconfigLength, "\"%s\", ", this->progs[i].alias );
  122. if( res < 0 ) goto l_sprintf_error;
  123. jsconfigLength += res;
  124. }
  125. }
  126. res = sprintf( jsconfig + jsconfigLength, "null ];\n" );
  127. if( res < 0 ) goto l_sprintf_error;
  128. jsconfigLength += res;
  129. /********* root nodes *********/
  130. //convert bytes to readable string
  131. readableSize( sizeStr, this->root.size );
  132. //replace html special chars in name
  133. res = htmlSpecialChars( this->root.desc + 1, tmpbuf, sizeof(tmpbuf) );
  134. if( res < 0 ) goto l_500;
  135. //generate root directory node
  136. if( (this->root.desc[0] & FILEDESC_TYPE_MASK) == FILEDESC_TYPE_DIR ){
  137. res = sprintf( page + pageLength,
  138. "<div class=\"nodeListItem\"><input type=\"hidden\" class=\"_dirId\" value=\"%i\">"
  139. "<span class=\"dirName\" onClick=\"dirIn( this );\">%s</span><small class=\"nodeSize\"> (%s)</small>\n"
  140. "<div class=\"dirExpander\"></div><div class=\"nodeInfoBlock\">\n",
  141. 0,//index
  142. tmpbuf, //name
  143. sizeStr //size
  144. );
  145. if( res < 0 ) goto l_sprintf_error;
  146. pageLength += res;
  147. //generate root file node
  148. }else{
  149. res = sprintf( page + pageLength,
  150. "<div class=\"nodeListItem\"><span class=\"fileName\">%s </span>"
  151. "<small class=\"nodeSize\"> (%s)</small><div class=\"nodeInfoBlock\">",
  152. tmpbuf, sizeStr
  153. );
  154. if( res < 0 ) goto l_sprintf_error;
  155. pageLength += res;
  156. //generate download links for case if browser not support js
  157. if( hasRaw && (this->root.desc[0] & FILEDESC_TYPE_MASK) == FILEDESC_TYPE_FILE ){
  158. if( hasFile ){
  159. res = sprintf( page + pageLength, "<a class=\"nodeInfoLink\" target=\"_blank\" href=\"download/0/view\">open</a>\n" );
  160. if( res < 0 ) goto l_sprintf_error;
  161. pageLength += res;
  162. }
  163. res = sprintf( page + pageLength, "<a class=\"nodeInfoLink\" target=\"_blank\" href=\"download/0/\">raw</a>\n" );
  164. if( res < 0 ) goto l_sprintf_error;
  165. pageLength += res;
  166. for( i = 0; this->progs[i].name; i++ ){
  167. if( !this->progs[i].path ) continue;
  168. if( this->progs[i].type != PROGDESC_TYPE_COMPRESSOR ) continue;
  169. res = sprintf( page + pageLength, "<a class=\"nodeInfoLink\" target=\"_blank\" href=\"download/0/%s\">.%s</a>\n",
  170. this->progs[i].alias, this->progs[i].alias
  171. );
  172. if( res < 0 ) goto l_sprintf_error;
  173. pageLength += res;
  174. }
  175. }
  176. }
  177. //generate download links for case if browser not support js
  178. if( hasTar ){
  179. res = sprintf( page + pageLength, "<a class=\"nodeInfoLink\" target=\"_blank\" href=\"download/0/tar\">.tar</a>\n" );
  180. if( res < 0 ) goto l_sprintf_error;
  181. pageLength += res;
  182. for( i = 0; this->progs[i].name; i++ ){
  183. if( !this->progs[i].path ) continue;
  184. if( this->progs[i].type != PROGDESC_TYPE_COMPRESSOR ) continue;
  185. res = sprintf( page + pageLength, "<a class=\"nodeInfoLink\" target=\"_blank\" href=\"download/0/%s.%s\">%s.%s</a>\n",
  186. "tar", this->progs[i].alias, "tar", this->progs[i].alias
  187. );
  188. if( res < 0 ) goto l_sprintf_error;
  189. pageLength += res;
  190. }
  191. }
  192. res = sprintf( page + pageLength, "</div></div>" );
  193. if( res < 0 ) goto l_sprintf_error;
  194. pageLength +=res;
  195. /********* generate header *********/
  196. //calculate content length
  197. contentLength += pagePart1Length;
  198. contentLength += jsconfigLength;
  199. contentLength += pagePart2Length;
  200. contentLength += pageLength;
  201. contentLength += pagePart3Length;
  202. //create headers
  203. headersLength = sprintf( headers,
  204. "%s 200 OK\r\n" //this->protocol
  205. "Content-Type: text/html; charset=utf-8\r\n"
  206. "Content-Length: %i\r\n" //contentLength
  207. "\r\n",
  208. this->protocol, contentLength
  209. );
  210. /********* send page *********/
  211. //this is vertor write experiment
  212. //setup vector
  213. fullPage[0].iov_base = headers; fullPage[0].iov_len = headersLength;
  214. fullPage[1].iov_base = (void*)pagePart1; fullPage[1].iov_len = pagePart1Length;
  215. fullPage[2].iov_base = jsconfig; fullPage[2].iov_len = jsconfigLength;
  216. fullPage[3].iov_base = (void*)pagePart2; fullPage[3].iov_len = pagePart2Length;
  217. fullPage[4].iov_base = page; fullPage[4].iov_len = pageLength;
  218. fullPage[5].iov_base = (void*)pagePart3; fullPage[5].iov_len = pagePart3Length;
  219. //calculate total len
  220. for( i = 0; i < 6; i++ ){
  221. fullPageLen += fullPage[i].iov_len;
  222. }
  223. //send full papge
  224. res = writev( this->clientSocket, fullPage, 6 );
  225. if( res != fullPageLen ){
  226. perror( "shareBrowserHtml writev" );
  227. return -1;
  228. }
  229. /*
  230. //send headers
  231. res = sendData( this->clientSocket, headers, headersLength );
  232. if( res ) return res;
  233. //send page part 1
  234. res = sendData( this->clientSocket, pagePart1, pagePart1Length );
  235. if( res ) return res;
  236. //send js config
  237. res = sendData( this->clientSocket, jsconfig, jsconfigLength );
  238. if( res ) return res;
  239. //send page part 2
  240. res = sendData( this->clientSocket, pagePart2, pagePart2Length );
  241. if( res ) return res;
  242. //send root nodes
  243. res = sendData( this->clientSocket, page, pageLength );
  244. if( res ) return res;
  245. //send page part 3
  246. res = sendData( this->clientSocket, pagePart3, pagePart3Length );
  247. if( res ) return res;
  248. */
  249. printf( "<<< 200 OK.\n" );
  250. return 0;
  251. l_sprintf_error:
  252. perror( "sprintf" );
  253. l_500:
  254. return httpSendStatus( this, 500, "Internal server error." );
  255. }
  256. int loadNodesBackend( struct instance* this, size_t uriOffset ){
  257. int res;
  258. long long int i;
  259. int parseUriRes;
  260. int headersLength;
  261. char tmpbuf[4096];
  262. int pageLength;
  263. char page[10240];
  264. struct file_db* nodes;
  265. long long int nodeIndex;
  266. nodes = &this->root;
  267. /********* search requested node in index *********/
  268. l_uriParseNext:
  269. //parse uri
  270. parseUriRes = parserCore( this->uri, '/', tmpbuf, sizeof(tmpbuf), &uriOffset );
  271. if( parseUriRes < 0 ) goto l_400;
  272. //try read int from uri part
  273. res = sscanf( tmpbuf, "%lli", &nodeIndex );
  274. if( res != 1 ) goto l_400;
  275. //search requested node in nodes tree
  276. for( i = 0; i != nodeIndex; i++ ){
  277. if( nodes[i].desc[0] & FILEDESC_LAST_FLAG ) goto l_403;
  278. }
  279. if( parseUriRes ){
  280. //some check before "open" node
  281. if( (nodes[nodeIndex].desc[0] & FILEDESC_TYPE_MASK) != FILEDESC_TYPE_DIR ) goto l_403;
  282. if( !nodes[nodeIndex].entries ) goto l_403;
  283. //"open" node
  284. nodes = nodes[nodeIndex].entries;
  285. i++;
  286. goto l_uriParseNext;
  287. }
  288. //set pointer to entries of requested node
  289. nodes = nodes[nodeIndex].entries;
  290. /********* http headers *********/
  291. //create headers
  292. headersLength = sprintf( tmpbuf,
  293. "%s 200 OK\r\n" //this->protocol
  294. "Content-Type: text/html; charset=utf-8\r\n"
  295. "\r\n",
  296. this->protocol
  297. );
  298. //send headers
  299. res = sendData( this->clientSocket, tmpbuf, headersLength );
  300. if( res ) return res;
  301. /********* send node info *********/
  302. //if node has not child nodes
  303. if( !nodes ){
  304. res = sendData( this->clientSocket, cnEmptyDir, sizeof(cnEmptyDir) - 1 );
  305. if( res ) return res;
  306. goto l_200;
  307. }
  308. //read and send info about node entries
  309. i = 0;
  310. l_generateNextCn:
  311. //replace html special chars in name
  312. res = htmlSpecialChars( nodes[i].desc + 1, tmpbuf, sizeof(tmpbuf) );
  313. if( res < 0 ) goto l_500;
  314. //generate cn( nodeType, nodeId, nodeName, nodeSize );
  315. pageLength = sprintf( page, "cn(%i,%lli,\"%s\",%li);",
  316. (nodes[i].desc[0] & FILEDESC_TYPE_MASK), //node type
  317. i, //index id
  318. tmpbuf, //node name
  319. nodes[i].size //node size
  320. );
  321. //send node entry
  322. res = sendData( this->clientSocket, page, pageLength );
  323. if( res ) return res;
  324. //if this not last, then generate next cn function
  325. if( !(nodes[i].desc[0] & FILEDESC_LAST_FLAG) ){
  326. i++;
  327. goto l_generateNextCn;
  328. }
  329. l_200:
  330. printf( "<<< 200 OK.\n" );
  331. return 0;
  332. l_400: return httpSendStatus( this, 400, "Bad request." );
  333. l_403: return httpSendStatus( this, 403, "Access denied." );
  334. l_500: return httpSendStatus( this, 500, "Internal server error." );
  335. }
  336. static int _htmlDownloadHandler( struct instance* this, size_t uriOffset, long long int* depth ){
  337. int res;
  338. int uriParseRes;
  339. char uriPart[32];
  340. long long int i;
  341. long long int nodeIndex;
  342. struct file_db* nodes;
  343. struct file_db* targetNode;
  344. struct stat statbuf;
  345. //init some vars
  346. nodes = &this->root;
  347. targetNode = NULL;
  348. *depth = 0;
  349. l_parseUri:
  350. //parse uri
  351. uriParseRes = parserCore( this->uri, '/', uriPart, sizeof(uriPart), &uriOffset );
  352. if( uriParseRes < 0 ) goto l_400;
  353. //if this last uri part, then start archiver
  354. if( !uriParseRes ){
  355. if( !targetNode ) goto l_400;
  356. return unixDownloadHandler( this, targetNode, uriPart );
  357. }
  358. //try convert string to node index
  359. res = sscanf( uriPart, "%lli", &nodeIndex );
  360. if( res != 1 ) goto l_400;
  361. //check current node has childs
  362. if( !nodes ) goto l_403;
  363. //search requested node
  364. for( i = 0; i != nodeIndex; i++ ){
  365. if( nodes[i].desc[0] & FILEDESC_LAST_FLAG ) goto l_403;
  366. }
  367. if( targetNode ){
  368. //check type in our index is dir before open
  369. if( (targetNode->desc[0] & FILEDESC_TYPE_MASK) != FILEDESC_TYPE_DIR ) goto l_403;
  370. //check type on disk is dir before open
  371. res = lstat( targetNode->desc + 1, &statbuf);
  372. if( res < 0 || (statbuf.st_mode & S_IFMT) != S_IFDIR ) goto l_500;
  373. //cd into dir
  374. res = chdir( targetNode->desc + 1 );
  375. if( res ){
  376. perror( "chdir" );
  377. printf( "FATAL: Cannot open indexed folder: %s\n", targetNode->desc + 1 );
  378. goto l_500;
  379. }
  380. //increase depth count
  381. (*depth)++;
  382. }
  383. //set pointers
  384. targetNode = &nodes[nodeIndex];
  385. nodes = nodes[nodeIndex].entries;
  386. //parse next uri part
  387. goto l_parseUri;
  388. l_400: return httpSendStatus( this, 400, "Bad request." );
  389. l_403: return httpSendStatus( this, 403, "Access denied." );
  390. l_500:
  391. httpSendStatus( this, 500, "Internal server error." );
  392. return -2;
  393. }
  394. int htmlDownloadHandler( struct instance* this, size_t uriOffset ){
  395. int res;
  396. long long int depth;
  397. depth = 0;
  398. res = _htmlDownloadHandler( this, uriOffset, &depth );
  399. if( res < -1 ) return res;
  400. while( depth ){
  401. res = chdir( "../" );
  402. if( res ){
  403. perror( "FATAL: Cannot exit from directory" );
  404. return -2;
  405. }
  406. depth--;
  407. }
  408. return res;
  409. }