| /* |
| * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com> |
| * All rights reserved |
| * |
| * "THE BEER-WARE LICENSE" (Revision 42): |
| * Sergey Lyubka wrote this file. As long as you retain this notice you |
| * can do whatever you want with this stuff. If we meet some day, and you think |
| * this stuff is worth it, you can buy me a beer in return. |
| */ |
| |
| #include "shttpd_defs.h" |
| |
| /* |
| * For a given PUT path, create all intermediate subdirectories |
| * for given path. Return 0 if the path itself is a directory, |
| * or -1 on error, 1 if OK. |
| */ |
| int |
| put_dir(const char *path) |
| { |
| char buf[FILENAME_MAX]; |
| const char *s, *p; |
| struct stat st; |
| size_t len; |
| |
| for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) { |
| len = p - path; |
| assert(len < sizeof(buf)); |
| (void) memcpy(buf, path, len); |
| buf[len] = '\0'; |
| |
| /* Try to create intermediate directory */ |
| if (my_stat(buf, &st) == -1 && my_mkdir(buf, 0755) != 0) |
| return (-1); |
| |
| /* Is path itself a directory ? */ |
| if (p[1] == '\0') |
| return (0); |
| } |
| |
| return (1); |
| } |
| |
| static int |
| read_dir(struct stream *stream, void *buf, size_t len) |
| { |
| struct dirent *dp = NULL; |
| char file[FILENAME_MAX], line[FILENAME_MAX + 512], |
| size[64], mod[64]; |
| struct stat st; |
| struct conn *c = stream->conn; |
| int n, nwritten = 0; |
| const char *slash = ""; |
| |
| assert(stream->chan.dir.dirp != NULL); |
| assert(stream->conn->uri[0] != '\0'); |
| |
| do { |
| if (len < sizeof(line)) |
| break; |
| |
| if ((dp = readdir(stream->chan.dir.dirp)) == NULL) { |
| stream->flags |= FLAG_CLOSED; |
| break; |
| } |
| DBG(("read_dir: %s", dp->d_name)); |
| |
| /* Do not show current dir and passwords file */ |
| if (strcmp(dp->d_name, ".") == 0 || |
| strcmp(dp->d_name, HTPASSWD) == 0) |
| continue; |
| |
| (void) snprintf(file, sizeof(file), |
| "%s%s%s", stream->chan.dir.path, slash, dp->d_name); |
| (void) my_stat(file, &st); |
| if (S_ISDIR(st.st_mode)) { |
| snprintf(size,sizeof(size),"%s","<DIR>"); |
| } else { |
| if (st.st_size < 1024) |
| (void) snprintf(size, sizeof(size), |
| "%lu", (unsigned long) st.st_size); |
| else if (st.st_size < 1024 * 1024) |
| (void) snprintf(size, sizeof(size), "%luk", |
| (unsigned long) (st.st_size >> 10) + 1); |
| else |
| (void) snprintf(size, sizeof(size), |
| "%.1fM", (float) st.st_size / 1048576); |
| } |
| (void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", |
| localtime(&st.st_mtime)); |
| |
| n = snprintf(line, sizeof(line), |
| "<tr><td><a href=\"%s%s%s\">%s%s</a></td>" |
| "<td> %s</td><td> %s</td></tr>\n", |
| c->uri, slash, dp->d_name, dp->d_name, |
| S_ISDIR(st.st_mode) ? "/" : "", mod, size); |
| (void) memcpy(buf, line, n); |
| buf = (char *) buf + n; |
| nwritten += n; |
| len -= n; |
| } while (dp != NULL); |
| |
| return (nwritten); |
| } |
| |
| static void |
| close_dir(struct stream *stream) |
| { |
| assert(stream->chan.dir.dirp != NULL); |
| assert(stream->chan.dir.path != NULL); |
| (void) closedir(stream->chan.dir.dirp); |
| free(stream->chan.dir.path); |
| } |
| |
| void |
| get_dir(struct conn *c) |
| { |
| if ((c->loc.chan.dir.dirp = opendir(c->loc.chan.dir.path)) == NULL) { |
| (void) free(c->loc.chan.dir.path); |
| send_server_error(c, 500, "Cannot open directory"); |
| } else { |
| c->loc.io.head = snprintf(c->loc.io.buf, c->loc.io.size, |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Type: text/html; charset=utf-8\r\n\r\n" |
| "<html><head><title>Index of %s</title>" |
| "<style>th {text-align: left;}</style></head>" |
| "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">" |
| "<tr><th>Name</th><th>Modified</th><th>Size</th></tr>" |
| "<tr><td colspan=\"3\"><hr></td></tr>", |
| c->uri, c->uri); |
| io_clear(&c->rem.io); |
| c->status = 200; |
| c->loc.io_class = &io_dir; |
| c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY; |
| } |
| } |
| |
| const struct io_class io_dir = { |
| "dir", |
| read_dir, |
| NULL, |
| close_dir |
| }; |