/* * output a directory index. return 1 if it actually did something.. */ int bozo_dir_index(bozo_httpreq_t *request, const char *dirpath, int isindex) { bozohttpd_t *httpd = request->hr_httpd; struct stat sb; struct dirent **de, **deo; struct tm *tm; DIR *dp; char buf[MAXPATHLEN]; char spacebuf[48]; char *file = NULL, *printname = NULL; int l, k, j, i; if (!isindex || !httpd->dir_indexing) return 0; if (strlen(dirpath) <= strlen(httpd->index_html)) dirpath = "."; else { file = bozostrdup(httpd, request, dirpath); file[strlen(file) - strlen(httpd->index_html)] = '\0'; dirpath = file; } debug((httpd, DEBUG_FAT, "bozo_dir_index: dirpath ``%s''", dirpath)); if (stat(dirpath, &sb) < 0 || (dp = opendir(dirpath)) == NULL) { if (errno == EPERM) (void)bozo_http_error(httpd, 403, request, "no permission to open directory"); else if (errno == ENOENT) (void)bozo_http_error(httpd, 404, request, "no file"); else (void)bozo_http_error(httpd, 500, request, "open directory"); goto done; /* NOTREACHED */ } bozo_printf(httpd, "%s 200 OK\r\n", request->hr_proto); if (request->hr_proto != httpd->consts.http_09) { bozo_print_header(request, NULL, "text/html", ""); bozo_printf(httpd, "\r\n"); } bozo_flush(httpd, stdout); if (request->hr_method == HTTP_HEAD) { closedir(dp); goto done; } #ifndef NO_USER_SUPPORT if (request->hr_user) { bozoasprintf(httpd, &printname, "~%s/%s", request->hr_user, request->hr_file); } else printname = bozostrdup(httpd, request, request->hr_file); #else printname = bozostrdup(httpd, request, request->hr_file); #endif /* !NO_USER_SUPPORT */ bozo_printf(httpd, "<html><head><title>Index of %s</title></head>\r\n", printname); bozo_printf(httpd, "<body><h1>Index of %s</h1>\r\n", printname); bozo_printf(httpd, "<pre>\r\n"); #define NAMELEN 40 #define LMODLEN 19 bozo_printf(httpd, "Name " "Last modified " "Size\n"); bozo_printf(httpd, "</pre>"); directory_hr(httpd); bozo_printf(httpd, "<pre>"); for (j = k = scandir(dirpath, &de, NULL, alphasort), deo = de; j--; de++) { int nostat = 0; char *name = (*de)->d_name; char *urlname, *htmlname; if (strcmp(name, ".") == 0 || (strcmp(name, "..") != 0 && httpd->hide_dots && name[0] == '.')) continue; snprintf(buf, sizeof buf, "%s/%s", dirpath, name); if (stat(buf, &sb)) nostat = 1; l = 0; urlname = bozo_escape_rfc3986(httpd, name, 0); htmlname = bozo_escape_html(httpd, name); if (htmlname == NULL) htmlname = name; if (strcmp(name, "..") == 0) { bozo_printf(httpd, "<a href=\"../\">"); l += bozo_printf(httpd, "Parent Directory"); } else if (S_ISDIR(sb.st_mode)) { bozo_printf(httpd, "<a href=\"%s/\">", urlname); l += bozo_printf(httpd, "%s/", htmlname); } else if (strchr(name, ':') != NULL) { /* RFC 3986 4.2 */ bozo_printf(httpd, "<a href=\"./%s\">", urlname); l += bozo_printf(httpd, "%s", htmlname); } else { bozo_printf(httpd, "<a href=\"%s\">", urlname); l += bozo_printf(httpd, "%s", htmlname); } if (htmlname != name) free(htmlname); bozo_printf(httpd, "</a>"); /* NAMELEN spaces */ /*LINTED*/ assert(/*CONSTCOND*/sizeof(spacebuf) > NAMELEN); i = (l < NAMELEN) ? (NAMELEN - l) : 0; i++; memset(spacebuf, ' ', (size_t)i); spacebuf[i] = '\0'; bozo_printf(httpd, "%s", spacebuf); l += i; if (nostat) bozo_printf(httpd, "? ?"); else { tm = gmtime(&sb.st_mtime); strftime(buf, sizeof buf, "%d-%b-%Y %R", tm); l += bozo_printf(httpd, "%s", buf); /* LMODLEN spaces */ /*LINTED*/ assert(/*CONSTCOND*/sizeof(spacebuf) > LMODLEN); i = (l < (LMODLEN+NAMELEN+1)) ? ((LMODLEN+NAMELEN+1) - l) : 0; i++; memset(spacebuf, ' ', (size_t)i); spacebuf[i] = '\0'; bozo_printf(httpd, "%s", spacebuf); bozo_printf(httpd, "%12llukB", (unsigned long long)sb.st_size >> 10); } bozo_printf(httpd, "\r\n"); } closedir(dp); while (k--) free(deo[k]); free(deo); bozo_printf(httpd, "</pre>"); directory_hr(httpd); bozo_printf(httpd, "</body></html>\r\n\r\n"); bozo_flush(httpd, stdout); done: free(file); free(printname); return 1; }
/* * bozo_user_transform does this: * - chdir's /~user/public_html * - returns the rest of the file, index.html appended if required * - returned malloced file to serve in request->hr_file, * ala transform_request(). * * transform_request() is supposed to check that we have user support * enabled. */ int bozo_user_transform(bozo_httpreq_t *request, int *isindex) { bozohttpd_t *httpd = request->hr_httpd; char c, *s, *file = NULL; struct passwd *pw; *isindex = 0; if ((s = strchr(request->hr_file + 2, '/')) != NULL) { *s++ = '\0'; c = s[strlen(s)-1]; *isindex = (c == '/' || c == '\0'); } debug((httpd, DEBUG_OBESE, "looking for user %s", request->hr_file + 2)); pw = getpwnam(request->hr_file + 2); /* fix this up immediately */ if (s) s[-1] = '/'; if (pw == NULL) { (void)bozo_http_error(httpd, 404, request, "no such user"); return 0; } debug((httpd, DEBUG_OBESE, "user %s dir %s/%s uid %d gid %d", pw->pw_name, pw->pw_dir, httpd->public_html, pw->pw_uid, pw->pw_gid)); if (chdir(pw->pw_dir) < 0) { bozo_warn(httpd, "chdir1 error: %s: %s", pw->pw_dir, strerror(errno)); (void)bozo_http_error(httpd, 404, request, "can't chdir to homedir"); return 0; } if (chdir(httpd->public_html) < 0) { bozo_warn(httpd, "chdir2 error: %s: %s", httpd->public_html, strerror(errno)); (void)bozo_http_error(httpd, 404, request, "can't chdir to public_html"); return 0; } if (s == NULL || *s == '\0') { file = bozostrdup(httpd, httpd->index_html); } else { file = bozomalloc(httpd, strlen(s) + (*isindex ? strlen(httpd->index_html) + 1 : 1)); strcpy(file, s); if (*isindex) strcat(file, httpd->index_html); } /* see transform_request() */ if (*file == '/' || strcmp(file, "..") == 0 || strstr(file, "/..") || strstr(file, "../")) { (void)bozo_http_error(httpd, 403, request, "illegal request"); free(file); return 0; } if (bozo_auth_check(request, file)) { free(file); return 0; } free(request->hr_file); request->hr_file = file; debug((httpd, DEBUG_FAT, "transform_user returning %s under %s", file, pw->pw_dir)); return 1; }