63
63
const char *DIR_REDIRECT_FORMAT = "HTTP/1.1 301 Moved Permanently\r\n"
64
"Location: http://%s%s/\r\n"
64
"Location: //%s%s/\r\n"
65
65
"Content-Length: 0\r\n"
69
69
// TODO: confirm that we are actually doing the GMT time right
70
const char *RFC_822_TIME = "%a, %d %b %Y %H:%M:%S GMT";
70
static const char *RFC_822_TIME = "%a, %d %b %Y %H:%M:%S GMT";
72
72
static int filerecord_cache_lookup(void *data, void *key) {
73
73
bstring request_path = (bstring) key;
80
80
FileRecord_release((FileRecord *) data);
83
static inline int get_file_real_size(FileRecord *fr)
85
// TODO: this is the total suck we'll redesign this away
86
int fd = open(bdata(fr->full_path), O_RDONLY);
87
check(fd >= 0, "Failed to open file but stat worked: %s", bdata(fr->full_path));
89
fr->file_size = lseek(fd, 0L, SEEK_END);
90
check(fr->file_size >= 0, "Failed to seek end of file: %s", bdata(fr->full_path));
91
lseek(fd, 0L, SEEK_SET);
84
104
FileRecord *Dir_find_file(bstring path, bstring default_type)
86
106
FileRecord *fr = calloc(sizeof(FileRecord), 1);
87
const char *p = bdata(path);
91
110
// We set the number of users here. If we cache it, we can add one later
112
fr->full_path = path;
94
int rc = stat(p, &fr->sb);
95
check(rc == 0, "File stat failed: %s", bdata(path));
114
int rc = stat(bdata(fr->full_path), &fr->sb);
115
check(rc == 0, "File stat failed: %s", bdata(fr->full_path));
97
117
if(S_ISDIR(fr->sb.st_mode)) {
103
fr->fd = open(p, O_RDONLY);
104
check(fr->fd >= 0, "Failed to open file but stat worked: %s", bdata(path));
106
fr->file_size = lseek(fr->fd, 0L, SEEK_END);
107
lseek(fr->fd, 0L, SEEK_SET);
122
check(get_file_real_size(fr) == 0, "Failed to setup the file record for %s", bdata(fr->full_path));
109
123
fr->loaded = time(NULL);
111
125
fr->last_mod = bStrfTime(RFC_822_TIME, gmtime(&fr->sb.st_mtime));
148
162
long long int Dir_stream_file(FileRecord *file, Connection *conn)
150
164
long long int sent = 0;
152
167
int rc = Dir_send_header(file, conn);
153
168
check_debug(rc, "Failed to write header to socket.");
155
sent = IOBuf_stream_file(conn->iob, file->fd, file->file_size);
170
fd = open(bdata(file->full_path), O_RDONLY);
171
check(fd >= 0, "Failed to open file: %s", bdata(file->full_path));
173
sent = IOBuf_stream_file(conn->iob, fd, file->file_size);
157
176
return file->file_size;
179
if(fd >= 0) fdclose(fd);
264
static inline char *url_decode(const char *in, char *out)
266
const char *cur; /* will seek % in input */
267
char d1; /* will contain candidate for 1st digit */
268
char d2; /* will contain candidate for 2nd digit */
269
char *res = out; /* just for convienience */
282
/* One character left in input */
289
/* Two characters left in input */
297
/* Legal escape sequence */
298
if(*cur=='%' && isxdigit(d1) && isxdigit(d2)) {
245
327
static inline int normalize_path(bstring target)
253
335
check_mem(path_buf);
338
url_decode((const char *)(bdata(target)), path_buf);
339
bassigncstr(target, path_buf);
256
341
char *normalized = realpath((const char *)(bdata(target)), path_buf);
257
342
check_debug(normalized, "Failed to normalize path: %s", bdata(target));
291
376
if(difftime(now, file->loaded) > dir->cache_ttl) {
292
377
int rcstat = stat(p, &sb);
294
if(rcstat != 0 || file->sb.st_mtime != sb.st_mtime || file->sb.st_size != sb.st_size) {
380
file->sb.st_mtime != sb.st_mtime ||
381
file->sb.st_ctime != sb.st_ctime ||
382
file->sb.st_uid != sb.st_uid ||
383
file->sb.st_gid != sb.st_gid ||
384
file->sb.st_mode != sb.st_mode ||
385
file->sb.st_size != sb.st_size ||
386
file->sb.st_ino != sb.st_ino ||
387
file->sb.st_dev != sb.st_dev
295
389
Cache_evict_object(dir->fr_cache, file);
309
403
FileRecord *file = NULL;
310
404
bstring target = NULL;
406
check(blength(prefix) <= blength(path),
407
"Path '%s' is shorter than prefix '%s', not allowed.", bdata(path), bdata(prefix));
312
409
check(Dir_lazy_normalize_base(dir) == 0, "Failed to normalize base path when requesting %s",
337
434
bdata(dir->index_file));
338
435
} else if(biseq(prefix, path)) {
339
436
target = bformat("%s%s", bdata(dir->normalized_base), bdata(path));
342
438
target = bformat("%s/%s", bdata(dir->normalized_base), bdataofs(path, blength(prefix)));
473
569
rc = Response_send_status(conn, &HTTP_405);
474
570
check_debug(rc == blength(&HTTP_405), "Failed to send 405 to client.");
572
} else if (blength(prefix) > blength(path)) {
573
req->status_code = 404;
574
rc = Response_send_status(conn, &HTTP_404);
575
check_debug(rc == blength(&HTTP_404), "Failed to send 404 to client.");
477
578
file = Dir_resolve_file(dir, prefix, path);
478
579
resp = Dir_calculate_response(req, file);