data.c - quark - quark web server
(HTM) git clone git://git.suckless.org/quark
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) LICENSE
---
data.c (4933B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <dirent.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <time.h>
8 #include <unistd.h>
9
10 #include "data.h"
11 #include "http.h"
12 #include "util.h"
13
14 enum status (* const data_fct[])(const struct response *,
15 struct buffer *, size_t *) = {
16 [RESTYPE_DIRLISTING] = data_prepare_dirlisting_buf,
17 [RESTYPE_ERROR] = data_prepare_error_buf,
18 [RESTYPE_FILE] = data_prepare_file_buf,
19 };
20
21 static int
22 compareent(const struct dirent **d1, const struct dirent **d2)
23 {
24 int v;
25
26 v = ((*d2)->d_type == DT_DIR ? 1 : -1) -
27 ((*d1)->d_type == DT_DIR ? 1 : -1);
28 if (v) {
29 return v;
30 }
31
32 return strcmp((*d1)->d_name, (*d2)->d_name);
33 }
34
35 static char *
36 suffix(int t)
37 {
38 switch (t) {
39 case DT_FIFO: return "|";
40 case DT_DIR: return "/";
41 case DT_LNK: return "@";
42 case DT_SOCK: return "=";
43 }
44
45 return "";
46 }
47
48 static void
49 html_escape(const char *src, char *dst, size_t dst_siz)
50 {
51 const struct {
52 char c;
53 char *s;
54 } escape[] = {
55 { '&', "&" },
56 { '<', "<" },
57 { '>', ">" },
58 { '"', """ },
59 { '\'', "'" },
60 };
61 size_t i, j, k, esclen;
62
63 for (i = 0, j = 0; src[i] != '\0'; i++) {
64 for (k = 0; k < LEN(escape); k++) {
65 if (src[i] == escape[k].c) {
66 break;
67 }
68 }
69 if (k == LEN(escape)) {
70 /* no escape char at src[i] */
71 if (j == dst_siz - 1) {
72 /* silent truncation */
73 break;
74 } else {
75 dst[j++] = src[i];
76 }
77 } else {
78 /* escape char at src[i] */
79 esclen = strlen(escape[k].s);
80
81 if (j >= dst_siz - esclen) {
82 /* silent truncation */
83 break;
84 } else {
85 memcpy(&dst[j], escape[k].s, esclen);
86 j += esclen;
87 }
88 }
89 }
90 dst[j] = '\0';
91 }
92
93 enum status
94 data_prepare_dirlisting_buf(const struct response *res,
95 struct buffer *buf, size_t *progress)
96 {
97 enum status s = 0;
98 struct dirent **e;
99 size_t i;
100 int dirlen;
101 char esc[PATH_MAX /* > NAME_MAX */ * 6]; /* strlen("&...;") <= 6 */
102
103 /* reset buffer */
104 memset(buf, 0, sizeof(*buf));
105
106 /* read directory */
107 if ((dirlen = scandir(res->internal_path, &e, NULL, compareent)) < 0) {
108 return S_FORBIDDEN;
109 }
110
111 if (*progress == 0) {
112 /* write listing header (sizeof(esc) >= PATH_MAX) */
113 html_escape(res->path, esc, MIN(PATH_MAX, sizeof(esc)));
114 if (buffer_appendf(buf,
115 "<!DOCTYPE html>\n<html>\n\t<head>"
116 "<title>Index of %s</title></head>\n"
117 "\t<body>\n\t\t<a href=\"..\">..</a>",
118 esc) < 0) {
119 s = S_REQUEST_TIMEOUT;
120 goto cleanup;
121 }
122 }
123
124 /* listing entries */
125 for (i = *progress; i < (size_t)dirlen; i++) {
126 /* skip hidden files, "." and ".." */
127 if (e[i]->d_name[0] == '.') {
128 continue;
129 }
130
131 /* entry line */
132 html_escape(e[i]->d_name, esc, sizeof(esc));
133 if (buffer_appendf(buf,
134 "<br />\n\t\t<a href=\"%s%s\">%s%s</a>",
135 esc,
136 (e[i]->d_type == DT_DIR) ? "/" : "",
137 esc,
138 suffix(e[i]->d_type))) {
139 /* buffer full */
140 break;
141 }
142 }
143 *progress = i;
144
145 if (*progress == (size_t)dirlen) {
146 /* listing footer */
147 if (buffer_appendf(buf, "\n\t</body>\n</html>\n") < 0) {
148 s = S_REQUEST_TIMEOUT;
149 goto cleanup;
150 }
151 (*progress)++;
152 }
153
154 cleanup:
155 while (dirlen--) {
156 free(e[dirlen]);
157 }
158 free(e);
159
160 return s;
161 }
162
163 enum status
164 data_prepare_error_buf(const struct response *res, struct buffer *buf,
165 size_t *progress)
166 {
167 /* reset buffer */
168 memset(buf, 0, sizeof(*buf));
169
170 if (*progress == 0) {
171 /* write error body */
172 if (buffer_appendf(buf,
173 "<!DOCTYPE html>\n<html>\n\t<head>\n"
174 "\t\t<title>%d %s</title>\n\t</head>\n"
175 "\t<body>\n\t\t<h1>%d %s</h1>\n"
176 "\t</body>\n</html>\n",
177 res->status, status_str[res->status],
178 res->status, status_str[res->status])) {
179 return S_INTERNAL_SERVER_ERROR;
180 }
181 (*progress)++;
182 }
183
184 return 0;
185 }
186
187 enum status
188 data_prepare_file_buf(const struct response *res, struct buffer *buf,
189 size_t *progress)
190 {
191 FILE *fp;
192 enum status s = 0;
193 ssize_t r;
194 size_t remaining;
195
196 /* reset buffer */
197 memset(buf, 0, sizeof(*buf));
198
199 /* open file */
200 if (!(fp = fopen(res->internal_path, "r"))) {
201 s = S_FORBIDDEN;
202 goto cleanup;
203 }
204
205 /* seek to lower bound + progress */
206 if (fseek(fp, res->file.lower + *progress, SEEK_SET)) {
207 s = S_INTERNAL_SERVER_ERROR;
208 goto cleanup;
209 }
210
211 /* read data into buf */
212 remaining = res->file.upper - res->file.lower + 1 - *progress;
213 while ((r = fread(buf->data + buf->len, 1,
214 MIN(sizeof(buf->data) - buf->len,
215 remaining), fp))) {
216 if (r < 0) {
217 s = S_INTERNAL_SERVER_ERROR;
218 goto cleanup;
219 }
220 buf->len += r;
221 *progress += r;
222 remaining -= r;
223 }
224
225 cleanup:
226 if (fp) {
227 fclose(fp);
228 }
229
230 return s;
231 }