@cryptotaxi247 / netdata-1 / commits / dfa045ae6

Redirect to index.html when a file is not found by web server (#15143)

* redirect to index.html when a file is not found * return index.html only when not found is likely a directory * recursively find the first path that works --------- Co-authored-by: Costa Tsaousis <costa@netdata.cloud>

Emmanuel Vasilakis committed Jun 14, 2023 at 21:37 UTC dfa045ae6c64b69871ea1a6c90bd07333d1118cb
1 file changed +71 -32
web/server/web_client.c
+71 -32
@@ -324,6 +324,64 @@ static inline int access_to_file_is_not_permitted(struct web_client *w, const ch
324
325 // Work around a bug in the CMocka library by removing this function during testing.
326 #ifndef REMOVE_MYSENDFILE
327 +
328 +static bool find_filename_to_serve(const char *filename, char *dst, size_t dst_len, struct stat *statbuf) {
329 + // copy the filename to our src buffer
330 + char path[FILENAME_MAX + 1];
331 + strncpyz(path, filename, FILENAME_MAX);
332 +
333 + bool strip = false;
334 + while(1) {
335 + if(*path)
336 + snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, path);
337 + else
338 + snprintfz(dst, dst_len, "%s", netdata_configured_web_dir);
339 +
340 + // internal_error(true, "WEBFILE: trying '%s', path '%s'", dst, path);
341 +
342 + strip = false;
343 + if (lstat(dst, statbuf) != 0)
344 + strip = true;
345 +
346 + if (!strip && (statbuf->st_mode & S_IFMT) == S_IFDIR) {
347 + // it is a directory
348 + // let's see if it has index.html in it
349 + if(*path)
350 + snprintfz(dst, dst_len, "%s/%s/index.html", netdata_configured_web_dir, path);
351 + else
352 + snprintfz(dst, dst_len, "%s/index.html", netdata_configured_web_dir);
353 +
354 + if (lstat(dst, statbuf) != 0 || (statbuf->st_mode & S_IFMT) == S_IFDIR)
355 + strip = true;
356 + }
357 +
358 + if(!strip && (statbuf->st_mode & S_IFMT) != S_IFREG)
359 + strip = true;
360 +
361 + if(strip) {
362 + char *s = path, *e = path;
363 + while(*e) e++; // find the terminator
364 + if(e > s) e--; // find the last character
365 +
366 + while(e >= s && *e != '/') *e-- = '\0'; // find the previous slash
367 + while(e >= s && *e == '/') *e-- = '\0'; // zero the slashes
368 +
369 + if(!*s || e <= s) {
370 + snprintfz(dst, dst_len, "%s/index.html", netdata_configured_web_dir);
371 + if(lstat(dst, statbuf) != 0)
372 + return false;
373 + else
374 + break;
375 + }
376 + }
377 + else
378 + break;
379 + }
380 +
381 + // internal_error(true, "WEBFILE: final '%s'", dst);
382 + return true;
383 +}
384 +
385 int mysendfile(struct web_client *w, char *filename) {
386 debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
387
@@ -355,60 +413,41 @@ int mysendfile(struct web_client *w, char *filename) {
413 }
414
415 // find the physical file on disk
358 - char webfilename[FILENAME_MAX + 1];
359 - snprintfz(webfilename, FILENAME_MAX, "%s/%s", netdata_configured_web_dir, filename);
360 -
416 + char web_filename[FILENAME_MAX + 1];
417 struct stat statbuf;
362 - int done = 0;
363 - while(!done) {
364 - // check if the file exists
365 - if (lstat(webfilename, &statbuf) != 0) {
366 - debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
367 - w->response.data->content_type = CT_TEXT_HTML;
368 - buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
369 - buffer_strcat_htmlescape(w->response.data, webfilename);
370 - return HTTP_RESP_NOT_FOUND;
371 - }
372 -
373 - if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
374 - snprintfz(webfilename, FILENAME_MAX, "%s/%s/index.html", netdata_configured_web_dir, filename);
375 - continue;
376 - }
377 -
378 - if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
379 - error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
380 - return access_to_file_is_not_permitted(w, webfilename);
381 - }
382 -
383 - done = 1;
418 + if(!find_filename_to_serve(filename, web_filename, FILENAME_MAX, &statbuf)) {
419 + w->response.data->content_type = CT_TEXT_HTML;
420 + buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
421 + buffer_strcat_htmlescape(w->response.data, web_filename);
422 + return HTTP_RESP_NOT_FOUND;
423 }
424
425 // open the file
387 - w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
426 + w->ifd = open(web_filename, O_NONBLOCK, O_RDONLY);
427 if(w->ifd == -1) {
428 w->ifd = w->ofd;
429
430 if(errno == EBUSY || errno == EAGAIN) {
392 - error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
431 + error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, web_filename);
432 w->response.data->content_type = CT_TEXT_HTML;
433 buffer_sprintf(w->response.header, "Location: /%s\r\n", filename);
434 buffer_strcat(w->response.data, "File is currently busy, please try again later: ");
396 - buffer_strcat_htmlescape(w->response.data, webfilename);
435 + buffer_strcat_htmlescape(w->response.data, web_filename);
436 return HTTP_RESP_REDIR_TEMP;
437 }
438 else {
400 - error("%llu: Cannot open file '%s'.", w->id, webfilename);
439 + error("%llu: Cannot open file '%s'.", w->id, web_filename);
440 w->response.data->content_type = CT_TEXT_HTML;
441 buffer_strcat(w->response.data, "Cannot open file: ");
403 - buffer_strcat_htmlescape(w->response.data, webfilename);
442 + buffer_strcat_htmlescape(w->response.data, web_filename);
443 return HTTP_RESP_NOT_FOUND;
444 }
445 }
446
447 sock_setnonblock(w->ifd);
448
410 - w->response.data->content_type = contenttype_for_filename(webfilename);
411 - debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, ifd %d, ofd %d).", w->id, webfilename, (int64_t)statbuf.st_size, w->ifd, w->ofd);
449 + w->response.data->content_type = contenttype_for_filename(web_filename);
450 + debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, ifd %d, ofd %d).", w->id, web_filename, (int64_t)statbuf.st_size, w->ifd, w->ofd);
451
452 w->mode = WEB_CLIENT_MODE_FILECOPY;
453 web_client_enable_wait_receive(w);