version 0.2.16
[fms.git] / libs / shttpd / io_dir.c
1 /*
2  * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
3  * All rights reserved
4  *
5  * "THE BEER-WARE LICENSE" (Revision 42):
6  * Sergey Lyubka wrote this file.  As long as you retain this notice you
7  * can do whatever you want with this stuff. If we meet some day, and you think
8  * this stuff is worth it, you can buy me a beer in return.
9  */
10
11 #include "defs.h"
12
13 /*
14  * For a given PUT path, create all intermediate subdirectories
15  * for given path. Return 0 if the path itself is a directory,
16  * or -1 on error, 1 if OK.
17  */
18 int
19 put_dir(const char *path)
20 {
21         char            buf[FILENAME_MAX];
22         const char      *s, *p;
23         struct stat     st;
24         size_t          len;
25
26         for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
27                 len = p - path;
28                 assert(len < sizeof(buf));
29                 (void) memcpy(buf, path, len);
30                 buf[len] = '\0';
31
32                 /* Try to create intermediate directory */
33                 if (my_stat(buf, &st) == -1 && my_mkdir(buf, 0755) != 0)
34                         return (-1);
35
36                 /* Is path itself a directory ? */
37                 if (p[1] == '\0')
38                         return (0);
39         }
40
41         return (1);
42 }
43
44 static int
45 read_dir(struct stream *stream, void *buf, size_t len)
46 {
47         struct dirent   *dp = NULL;
48         char            file[FILENAME_MAX], line[FILENAME_MAX + 512],
49                                 size[64], mod[64];
50         struct stat     st;
51         struct conn     *c = stream->conn;
52         int             n, nwritten = 0;
53         const char      *slash = "";
54
55         assert(stream->chan.dir.dirp != NULL);
56         assert(stream->conn->uri[0] != '\0');
57
58         do {
59                 if (len < sizeof(line))
60                         break;
61
62                 if ((dp = readdir(stream->chan.dir.dirp)) == NULL) {
63                         stream->flags |= FLAG_CLOSED;
64                         break;
65                 }
66                 DBG(("read_dir: %s", dp->d_name));
67
68                 /* Do not show current dir and passwords file */
69                 if (strcmp(dp->d_name, ".") == 0 ||
70                    strcmp(dp->d_name, HTPASSWD) == 0)
71                         continue;
72
73                 (void) my_snprintf(file, sizeof(file),
74                     "%s%s%s", stream->chan.dir.path, slash, dp->d_name);
75                 (void) my_stat(file, &st);
76                 if (S_ISDIR(st.st_mode)) {
77                         my_snprintf(size,sizeof(size),"%s","&lt;DIR&gt;");
78                 } else {
79                         if (st.st_size < 1024)
80                                 (void) my_snprintf(size, sizeof(size),
81                                     "%lu", (unsigned long) st.st_size);
82                         else if (st.st_size < 1024 * 1024)
83                                 (void) my_snprintf(size, sizeof(size), "%luk",
84                                     (unsigned long) (st.st_size >> 10)  + 1);
85                         else
86                                 (void) my_snprintf(size, sizeof(size),
87                                     "%.1fM", (float) st.st_size / 1048576);
88                 }
89                 (void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
90                         localtime(&st.st_mtime));
91
92                 n = my_snprintf(line, sizeof(line),
93                     "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
94                     "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
95                     c->uri, slash, dp->d_name, dp->d_name,
96                     S_ISDIR(st.st_mode) ? "/" : "", mod, size);
97                 (void) memcpy(buf, line, n);
98                 buf = (char *) buf + n;
99                 nwritten += n;
100                 len -= n;
101         } while (dp != NULL);
102
103         return (nwritten);
104 }
105
106 static void
107 close_dir(struct stream *stream)
108 {
109         assert(stream->chan.dir.dirp != NULL);
110         assert(stream->chan.dir.path != NULL);
111         (void) closedir(stream->chan.dir.dirp);
112         free(stream->chan.dir.path);
113 }
114
115 void
116 get_dir(struct conn *c)
117 {
118         if ((c->loc.chan.dir.dirp = opendir(c->loc.chan.dir.path)) == NULL) {
119                 (void) free(c->loc.chan.dir.path);
120                 send_server_error(c, 500, "Cannot open directory");
121         } else {
122                 c->loc.io.head = my_snprintf(c->loc.io.buf, c->loc.io.size,
123                     "HTTP/1.1 200 OK\r\n"
124                     "Connection: close\r\n"
125                     "Content-Type: text/html; charset=utf-8\r\n\r\n"
126                     "<html><head><title>Index of %s</title>"
127                     "<style>th {text-align: left;}</style></head>"
128                     "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
129                     "<tr><th>Name</th><th>Modified</th><th>Size</th></tr>"
130                     "<tr><td colspan=\"3\"><hr></td></tr>",
131                     c->uri, c->uri);
132                 io_clear(&c->rem.io);
133                 c->status = 200;
134                 c->loc.io_class = &io_dir;
135                 c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
136         }
137 }
138
139 const struct io_class   io_dir =  {
140         "dir",
141         read_dir,
142         NULL,
143         close_dir
144 };