1/* $NetBSD: dir-index-bozo.c,v 1.37 2024/04/26 20:27:12 maya Exp $ */ 2 3/* $eterna: dir-index-bozo.c,v 1.20 2011/11/18 09:21:15 mrg Exp $ */ 4 5/* 6 * Copyright (c) 1997-2022 Matthew R. Green 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer and 16 * dedication in the documentation and/or other materials provided 17 * with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 */ 32 33/* this code implements directory index generation for bozohttpd */ 34 35#ifndef NO_DIRINDEX_SUPPORT 36 37#include <sys/param.h> 38 39#include <dirent.h> 40#include <errno.h> 41#include <fcntl.h> 42#include <string.h> 43#include <stdlib.h> 44#include <time.h> 45#include <unistd.h> 46#include <assert.h> 47 48#include "bozohttpd.h" 49 50/* 51 * output a directory index. return 1 if it actually did something.. 52 */ 53int 54bozo_dir_index(bozo_httpreq_t *request, const char *dirpath, int isindex) 55{ 56 bozohttpd_t *httpd = request->hr_httpd; 57 struct stat sb; 58 struct dirent **de, **deo; 59 DIR *dp; 60 char buf[MAXPATHLEN]; 61 char *file = NULL, *printname = NULL, *p; 62 int k, j, fd; 63 ssize_t rlen; 64 65 if (!isindex || !httpd->dir_indexing) 66 return 0; 67 68 if (strlen(dirpath) <= strlen(httpd->index_html)) 69 dirpath = "."; 70 else { 71 file = bozostrdup(httpd, request, dirpath); 72 73 file[strlen(file) - strlen(httpd->index_html)] = '\0'; 74 dirpath = file; 75 } 76 debug((httpd, DEBUG_FAT, "bozo_dir_index: dirpath '%s'", dirpath)); 77 if (stat(dirpath, &sb) < 0 || 78 (dp = opendir(dirpath)) == NULL) { 79 if (errno == EPERM) 80 bozo_http_error(httpd, 403, request, 81 "no permission to open directory"); 82 else if (errno == ENOENT) 83 bozo_http_error(httpd, 404, request, "no file"); 84 else 85 bozo_http_error(httpd, 500, request, "open directory"); 86 goto done; 87 /* NOTREACHED */ 88 } 89 90 bozo_printf(httpd, "%s 200 OK\r\n", request->hr_proto); 91 92 if (request->hr_proto != httpd->consts.http_09) { 93 bozo_print_header(request, NULL, "text/html", ""); 94 bozo_printf(httpd, "\r\n"); 95 } 96 bozo_flush(httpd, stdout); 97 98 if (request->hr_method == HTTP_HEAD) { 99 closedir(dp); 100 goto done; 101 } 102 103#ifndef NO_USER_SUPPORT 104 if (request->hr_user) { 105 bozoasprintf(httpd, &printname, "~%s/%s", 106 request->hr_user, request->hr_file); 107 } else 108 printname = bozostrdup(httpd, request, request->hr_file); 109#else 110 printname = bozostrdup(httpd, request, request->hr_file); 111#endif /* !NO_USER_SUPPORT */ 112 if ((p = strstr(printname, httpd->index_html)) != NULL) { 113 if (strcmp(printname, httpd->index_html) == 0) 114 strcpy(printname, "/"); /* is ``slashdir'' */ 115 else 116 *p = '\0'; /* strip unwanted ``index_html'' */ 117 } 118 if ((p = bozo_escape_html(httpd, printname)) != NULL) { 119 free(printname); 120 printname = p; 121 } 122 123 bozo_printf(httpd, 124 "<!DOCTYPE html>\r\n" 125 "<html><head><meta charset=\"utf-8\"/>\r\n" 126 "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n" 127 "<style type=\"text/css\">\r\n" 128 "table {\r\n" 129 "\tborder-top: 1px solid black;\r\n" 130 "\tborder-bottom: 1px solid black;\r\n" 131 "}\r\n" 132 "th { background: aquamarine; }\r\n" 133 "tr:nth-child(even) { background: lavender; }\r\n" 134 "</style>\r\n"); 135 bozo_printf(httpd, "<title>Index of %s</title></head>\r\n", 136 printname); 137 bozo_printf(httpd, "<body><h1>Index of %s</h1>\r\n", 138 printname); 139 bozo_printf(httpd, 140 "<table cols=3>\r\n<thead>\r\n" 141 "<tr><th>Name<th>Last modified<th align=right>Size\r\n" 142 "<tbody>\r\n"); 143 144 for (j = k = scandir(dirpath, &de, NULL, alphasort), deo = de; 145 j-- > 0; de++) { 146 int nostat = 0; 147 char *name = (*de)->d_name; 148 char *urlname, *htmlname; 149 150 if (strcmp(name, ".") == 0 || 151 (strcmp(name, "..") != 0 && 152 httpd->hide_dots && name[0] == '.')) 153 continue; 154 155 if (bozo_check_special_files(request, name, false)) 156 continue; 157 158 snprintf(buf, sizeof buf, "%s/%s", dirpath, name); 159 if (stat(buf, &sb)) 160 nostat = 1; 161 162 urlname = bozo_escape_rfc3986(httpd, name, 0); 163 htmlname = bozo_escape_html(httpd, name); 164 if (htmlname == NULL) 165 htmlname = name; 166 bozo_printf(httpd, "<tr><td>"); 167 if (strcmp(name, "..") == 0) { 168 bozo_printf(httpd, "<a href=\"../\">"); 169 bozo_printf(httpd, "Parent Directory"); 170 } else if (!nostat && S_ISDIR(sb.st_mode)) { 171 bozo_printf(httpd, "<a href=\"%s/\">", urlname); 172 bozo_printf(httpd, "%s/", htmlname); 173 } else if (strchr(name, ':') != NULL) { 174 /* RFC 3986 4.2 */ 175 bozo_printf(httpd, "<a href=\"./%s\">", urlname); 176 bozo_printf(httpd, "%s", htmlname); 177 } else { 178 bozo_printf(httpd, "<a href=\"%s\">", urlname); 179 bozo_printf(httpd, "%s", htmlname); 180 } 181 if (htmlname != name) 182 free(htmlname); 183 bozo_printf(httpd, "</a>"); 184 185 if (nostat) 186 bozo_printf(httpd, "<td>?<td>?\r\n"); 187 else { 188 unsigned long long len; 189 190 strftime(buf, sizeof buf, "%d-%b-%Y %R", gmtime(&sb.st_mtime)); 191 bozo_printf(httpd, "<td>%s", buf); 192 193 len = ((unsigned long long)sb.st_size + 1023) / 1024; 194 bozo_printf(httpd, "<td align=right>%llukB", len); 195 } 196 bozo_printf(httpd, "\r\n"); 197 } 198 199 closedir(dp); 200 while (k--) 201 free(deo[k]); 202 free(deo); 203 bozo_printf(httpd, "</table>\r\n"); 204 if (httpd->dir_readme != NULL) { 205 if (httpd->dir_readme[0] == '/') 206 snprintf(buf, sizeof buf, "%s", httpd->dir_readme); 207 else 208 snprintf(buf, sizeof buf, "%s/%s", dirpath, httpd->dir_readme); 209 fd = open(buf, O_RDONLY); 210 if (fd != -1) { 211 bozo_flush(httpd, stdout); 212 do { 213 rlen = read(fd, buf, sizeof buf); 214 if (rlen <= 0) 215 break; 216 bozo_write(httpd, STDOUT_FILENO, buf, rlen); 217 } while (1); 218 close(fd); 219 } 220 } 221 bozo_printf(httpd, "</body></html>\r\n\r\n"); 222 bozo_flush(httpd, stdout); 223 224done: 225 free(file); 226 free(printname); 227 return 1; 228} 229#endif /* NO_DIRINDEX_SUPPORT */ 230