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